2

如果让你实现一个 promise ,你会怎么做?

图片描述

自己实现promise的大体思路

  1. 我们要明确我们需要一个异步的操作方法,满足异步回调。所以选择加入setTimeout 作为实现的基础, 让函数实现延迟触发。
  2. 保持一个原则,控制 promise 改变状态的只有 promise 构造函数里的 reslove 、 reject 函数。
  3. 链式调用的原理, 类似jQuery,它会在调用方法后, return this. 从而形成链式调用。所以我们采用在调用then(fn)、 catch(fn) 后 会返回一个新的 promise 对象, 然而 这个 promise 对象 受到 它的上级promise 对象的状态结果 和 fn 运行结果的控制。

知识点:
里面 应该 用到点 js 作用域 、 函数闭包、 继承 、 上下文 绑定知识、引用传递。

存在问题

cbList、rhList、 cs 这个三个, Promise 对象能直接访问, 如果对其直接操作可能造成 程序紊乱。

代码如有纰漏,望大家指正

var JcPromise = function (fn) {
    // 防止 用户 直接 更改 state
    var state = 'wait'
     // state 为 resolve 状态, 回调函数数组
    var cbList = []
     // state 为 reject 状态, 回调函数数组
    var rjList = []
    this.cbList = cbList
    this.rjList = rjList
    // 
    this.cs = undefined
    // 获取 promise 的状态
    this.getState = function () {
        return state
    }
    /* 函数闭包,函数 定义在里面, 防止 外面用户 直接 使用 resolve 和 reject; */
    // Promise成功触发 函数
    var reslove = function (data) {
        this.cs = data
        if (state !== 'wait') {
            return
        } else {
            state = 'solve'
            while (this.cbList.length) {
                cbList.shift()(data)
            }
        }
    }
   // Promise 拒绝 触发函数
    var reject = function (e) {
        this.cs = e
        if (state !== 'wait') {
            return
        } else {
            state = 'reject'
            while (rjList.length) {
                rjList.shift()(e)
            }
        }
    }
// 绑定函数 conext 及 this 为当前 promise对象
    reslove = reslove.bind(this)
    reject = reject.bind(this)
// 延迟 触发
    setTimeout(function () {
        fn(reslove, reject)
    }, 0)
}
JcPromise.prototype.then = function (fn) {
    var handleObj = {}
    var nextPromise = new JcPromise(function (r, j) {
        handleObj.r = r
        handleObj.j = j
    })
    var fixFn = function (data) {
        var result = null
        try {
            result = fn(data)
              // 判断result是不是 JcPromise实例。
            if (result instanceof JcPromise) {
                result.then(function (data) {
                    handleObj.r(data)
                }).catch(function (e) {
                    handleObj.j(e)
                })
            } else  {
                handleObj.r(result)
            }
        } catch (e){
            handleObj.j(e)
        }
    }
    //判断当前状态 如果 是 solve 直接 运行, 如果不是,酒吧 fixFn 推入 cbList 数组。
    if (this.getState() === 'solve') {
        setTimeout(function () {
            fixFn(this.cs)
        }, 0)
    } else {
        this.cbList.push(fixFn)
    }
    return nextPromise
}
JcPromise.prototype.catch = function (fn) {
    var handleObj = {}
    var nextPromise = new JcPromise(function (r, j) {
        handleObj.r = r
        handleObj.j = j
    })
    var fixFn = function (e) {
        var result = null
        try {
            result = fn(e)
            if (result instanceof JcPromise) {
                result.then(function (data) {
                    handleObj.r(data)
                }).catch(function (e) {
                    handleObj.j(e)
                })
            } else {
                handleObj.r(result)
            }
        } catch (e){
            handleObj.j(e)
        }
    }
    if (this.getState() === "reject") {
        setTimeout(function () {
            fixFn(this.cs)
        }, 0)
    } else {
        this.rjList.push(fixFn)
    }
    return nextPromise
}
// 测试代码
var p = new JcPromise(function(r, j) {
    setTimeout(function() {r(100)}, 3000)
}).then(data => {
    console.log('1', data)
    return new JcPromise((r, j) => {
        setTimeout(() => {
            r('hi')
        }, 3000)
    })
}).then(data => console.log('2', data)).then(function () {
    console.log('xxx', xx + 1)
}).catch(e => console.log(e)).then(data => console.log(data, 'end'))

demo 测试
第二版 jcPromise 实现

    var JcPromise = (function() {
        function JcPromise(fn) {
            fn = fn || noop;
            var statusList = ['start', 'pending', 'succeed', 'err'];
            var cbStatus = [0, 1];
            var status = statusList[0];
            var data = null;
            var err = null;
            var that = this;
            var successFn = [];
            var errFn = [];

            function resolve(d) {
                data = d;
                that._changeStatus(2);
            };

            function reject(e) {
                err = e;
                that._changeStatus(3);
            };
            this.getData = function() {
                return data;
            };
            this.getErr = function() {
                return err
            };
            this.getStatus = function() {
                return status
            };
            this._changeStatus = function(idx) {
                switch (status) {
                    case statusList[2]:
                    case statusList[3]:
                        {
                            return false
                        }
                };
                status = statusList[idx];
                if (status === statusList[3]) {
                    setTimeout(function() {
                        that._triggerCatch();
                    }, 0)
                }
                if (status === statusList[2]) {
                    setTimeout(function() {
                        that._triggerThen();
                    }, 0)
                }
            };
            this._pushThenCb = function(cb) {
                successFn.push({
                    status: cbStatus[0],
                    cb: cb
                });
                if (status === statusList[2]) {
                    this._triggerThen();
                }
            };
            this._pushCatchCb = function(cb) {
                errFn.push({
                    status: cbStatus[0],
                    cb: cb
                });
                if (status === statusList[3]) {
                    this._triggerCatch();
                }
            };
            this._triggerThen = function() {
                successFn.map(function(item) {
                    if (item.status === cbStatus[0]) {
                        item.cb(data);
                        item.status = cbStatus[1];
                    }
                })
            };
            this._triggerCatch = function() {
                errFn.map(function(item) {
                    if (item.status === cbStatus[0]) {
                        item.cb(err);
                        item.status = cbStatus[1];
                    }
                })
            };
            this._changeStatus(1);
            this.uuid = uuid++;
            try {
                fn(resolve, reject);
            } catch (e) {
                reject(e)
            }
            return this
        };
        JcPromise.fn = JcPromise.prototype;
        // 返回一个promise
        JcPromise.fn.then = function(cb) {
            var promiseR = null;
            var promiseJ = null;
            var result = null;
            var that = this;
            var fn = function() {
                setTimeout(function() {
                    try {
                        var data = that.getData();
                        result = cb(data);
                        if (typeof result === 'object' && result !== null && result.constructor === JcPromise) {
                            result.then(function(data) {
                                promiseR(data)
                            }).catch(function(e) {
                                promiseJ(e)
                            })
                        } else {
                            promiseR(result)
                        }
                    } catch (e) {
                        promiseJ(e)
                    }
                }, 0);
            };
            this._pushThenCb(fn);
            // 触发promise
            return new JcPromise(function(r, j) {
                promiseR = r;
                promiseJ = j;
            });
        };
        // 返回一个promise
        JcPromise.fn.catch = function(cb) {
            var promiseR = null;
            var promiseJ = null;
            var result = null;
            var that = this;
            var fn = function() {
                setTimeout(function() {
                    try {
                        var data = that.getErr();
                        result = cb(data);
                        if (typeof result === 'object' && result !== null && result.constructor === JcPromise) {
                            result.then(function(data) {
                                promiseR(data)
                            }).catch(function(e) {
                                promiseJ(e)
                            })
                        } else {
                            promiseR(result)
                        }
                    } catch (e) {
                        promiseJ(e)
                    }
                }, 0)
            };
            this._pushCatchCb(fn);
            // 触发promise
            return new JcPromise(function(r, j) {
                promiseR = r;
                promiseJ = j;
            });
        };
        return JcPromise
    })();

方糖先生
1.1k 声望1.8k 粉丝