简易版promise源码实现

 首先我们先看一下promise是如何使用的:

 <script>
            let p1=new Promise((resolve,reject)=>{
                    resolve()
            })
            p1.then(()=>{
                console.log(123)
            })
 </script>

  通过promise构建出来的对象有三种状态,Pending(进行中),Fulfilled(已成功),Rejected(已失败)

状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。

  通过then函数注册成功或者失败函数,promise内部调用resolve或者reject调用对应的函数。

; (function () {
    const status = {
        0: "pending",
        1: "fulfilled",
        2: "rejected"
    }
    class customePromise {
        constructor(func) {
            if (typeof func != "function") {
                throw TypeError("Promise resolver " + func + " is not a function")
            }
            func(this._resolve.bind(this), this._reject.bind(this));
        }
        _resolve(val) {
        }
        _reject(val) {
        }
    }
    window.customePromise = customePromise;
})();

 首先我们定义了一个customePromise的类,通过window暴露出去,通过前面promise调用我们可以看到参数是一个函数,该函数接受两个参数,resolve,reject,通过调用两个函数来执行对应的成功或者失败函数。第一步,我们在内部首先要判断传入是否为一个函数,才进行下一步操作。

; (function () {
    const status = {
        0: "pending",
        1: "fulfilled",
        2: "rejected"
    }
    class customePromise {
        constructor(func) {
            this._status = status[0];
            this.resolveArr = [];
            this.rejectArr = [];
            this.value="";
            if(typeof func!="function"){
                throw TypeError("Promise resolver "+ func+" is not a function")
            }
            try {
                func(this._resolve.bind(this), this._reject.bind(this));
            }
            catch (err) {
                this._reject(err)
            }
        }
        _resolve(val) {
        }
        _reject(val) {
        }
    }
    window.customePromise = customePromise;
})();

promise最大的好处就是他的捕获机制,一旦你在外部函数吃错都可以在内部可以捕获到,这样你外面的脚本还能继续往下执行,并通过失败函数可以打印出来。

一开始我们在构造函数开始执行的时候保存了这个对象状态为pending,然后在这个promise对象上挂载了两个属性用来保存成功和失败函数。下面我们来实现then函数

  then(resolveFunc, rejectFunc) {
            switch (this._status) {
                case "pending":
                    this.resolveArr.push(resolveFunc);
                    this.rejectArr.push(rejectFunc);
                    break;
                case "fulfilled":
                    resolvefunc(this.value)
                    break;
                case "rejected":
                    rejectfunc(this.value)
                    break;
            }
}

通过判断promise对象来决定函数是否执行,如果处于pending状态,就把成功失败函数存入对应的数组,如果状态已经改变,就执行对应的函数。下面实现成功失败函数的代码。

    _resolve(val) {
            setTimeout(() => {
                this.value=val;
                this._status = status[1];
                this.resolveArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }
        _reject(val) {
            setTimeout(() => {
                this.value=val;
                this._status = status[2];
                this.rejectArr.forEach(item => {
                    item(val);
                })
            }, 0)
 }

 注意下我们的promise属于微任务,他会让我们主线程的任务先执行,所以我们这里采用异步模拟。但这里还是存在一个问题,就是我们可以在外部多次调用,这明显是不允许,所以我们来优化下代码。

_resolve(val) {
            setTimeout(() => {
                if (this._status != status[0]) {
                    return;
                }
                this.value=val;
                this._status = status[1];
                this.resolveArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }
        _reject(val) {
            setTimeout(() => {
                if (this._status != status[0]) {
                    return;
                }
                this.value=val;
                this._status = status[2];
                this.rejectArr.forEach(item => {
                    item(val);
                })
            }, 0)
}

 用过jquery的同学都知道jquery是可以链式调用,同样我们的promise也是可以的,那就表示我们的then函数返回值同样也是个promise对象,下面我们来改写then函数。

   then(resolveFunc, rejectFunc) {
            let resolvefunc, rejectfunc;
            let _this = this;
            return new customePromise(function (resolve, reject) {
                resolvefunc = function (val) {
                    let value = resolveFunc(val);
                    resolve(value)
                }
                rejectfunc = function (val) {
                    let value = rejectFunc(val);
                    reject(value)
                }
                switch (_this._status) {
                    case "pending":
                        _this.resolveArr.push(resolvefunc);
                        _this.rejectArr.push(rejectfunc);
                        break;
                    case "fulfilled":
                        resolvefunc()
                        break;
                    case "rejected":
                        rejectfunc
                        break;
                }
            })

        }

  注意这里并不好理解,因为之前我们存入的事成功和失败函数,这里我们包装了一个函数并把then函数返回值promise对象的resolve函数也存入进去,这就表示如果我们第一个promise状态改变会接着触发第二个promise对象的执行,但需要注意如果我们函数的返回值同样是一个promise对象的话,我们必要要等待其状态改变才能触发他的下一个的then函数,这是一个难点,很不好理解,我们使用过把then函数返回的promise对象的resolve时间挂在函数内部的then上面,一旦内部函数状态改变会触发then函数的对应的事件,当然我们也是要加上容错处理,下面附上代码: 

 then(resolveFunc, rejectFunc) {
            let resolvefunc, rejectfunc;
            let _this = this;
            return new customePromise(function (resolve, reject) {
                resolvefunc = function (val) {
                    try {
                        if (typeof resolveFunc != "function") {
                            resolve(val)
                        }
                        else {
                            let value = resolveFunc(val);
                            if (value instanceof customePromise) {
                                value.then(resolve)
                            }
                            else {
                                resolve(value)
                            }
                        }
                    }
                    catch (err) {
                        console.log(err)
                        reject(err)
                    }
                }
                rejectfunc = function (val) {
                    try {
                        if (typeof rejectFunc != "function") {
                            resolve(val)
                        }
                        else {
                            let value = rejectFunc(val);
                            if (value instanceof customePromise) {
                                value.then(reject)
                            }
                            else {
                                reject(value)
                            }
                        }
                    }
                    catch (err) {
                        reject(err)
                    }

                }
                switch (_this._status) {
                    case "pending":
                        _this.resolveArr.push(resolvefunc);
                        _this.rejectArr.push(rejectfunc);
                        break;
                    case "fulfilled":
                        resolvefunc()
                        break;
                    case "rejected":
                        rejectfunc
                        break;
                }
            })
        }

 最后说一下resolve()参数如果是一个promise对象的话,必须要等待参数的状态改变才能触发他的then函数,原理跟上面的很相似。

 _resolve(val) {
            setTimeout(() => {
                if (val instanceof customePromise) {
                    val.then(()=>{
                        this._status = status[1];
                        this.resolveArr.forEach(item => {
                            item(val);
                        })
                    })
                    return;
                }
                if (this._status != status[0]) {
                    return;
                }
                this._status = status[1];
                this.resolveArr.forEach(item => {

                    item(val);
                })
            }, 0)
        }
        _reject(val) {
            setTimeout(() => {
                if (val instanceof customePromise) {
                    val.then(()=>{
                        this._status = status[2];
                        this.rejectArr.forEach(item => {
                            item(val);
                        })
                    })
                    return;
                }
                if (this._status != status[0]) {
                    return;
                }
                this._status = status[2];
                this.rejectArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }

最后附上所有的代码:

; (function () {
    const status = {
        0: "pending",
        1: "fulfilled",
        2: "rejected"
    }
    class customePromise {
        constructor(func) {
            this._status = status[0];
            this.resolveArr = [];
            this.rejectArr = [];
            if(typeof func!="function"){
                throw TypeError("Promise resolver "+ func+" is not a function")
            }
            try {
                func(this._resolve.bind(this), this._reject.bind(this));
            }
            catch (err) {
                this._reject(err)
            }
        }
        _resolve(val) {
            setTimeout(() => {
                if (val instanceof customePromise) {
                    val.then(()=>{
                        this._status = status[1];
                        this.resolveArr.forEach(item => {
                            item(val);
                        })
                    })
                    return;
                }
                if (this._status != status[0]) {
                    return;
                }
                this._status = status[1];
                this.resolveArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }
        _reject(val) {
            setTimeout(() => {
                if (val instanceof customePromise) {
                    val.then(()=>{
                        this._status = status[2];
                        this.rejectArr.forEach(item => {
                            item(val);
                        })
                    })
                    return;
                }
                if (this._status != status[0]) {
                    return;
                }
                this._status = status[2];
                this.rejectArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }
        then(resolveFunc, rejectFunc) {
            let resolvefunc, rejectfunc;
            let _this = this;
            return new customePromise(function (resolve, reject) {
                resolvefunc = function (val) {
                    try {
                        if (typeof resolveFunc != "function") {
                            resolve(val)
                        }
                        else {
                            let value = resolveFunc(val);
                            if (value instanceof customePromise) {
                                value.then(resolve)
                            }
                            else {
                                resolve(value)
                            }
                        }
                    }
                    catch (err) {
                        console.log(err)
                        reject(err)
                    }

                }
                rejectfunc = function (val) {
                    try {
                        if (typeof rejectFunc != "function") {
                            resolve(val)
                        }
                        else {
                            let value = rejectFunc(val);
                            if (value instanceof customePromise) {
                                value.then(reject)
                            }
                            else {
                                reject(value)
                            }
                        }
                    }
                    catch (err) {
                        reject(err)
                    }
                }
                switch (_this._status) {
                    case "pending":
                        _this.resolveArr.push(resolvefunc);
                        _this.rejectArr.push(rejectfunc);
                        break;
                    case "fulfilled":
                        resolvefunc()
                        break;
                    case "rejected":
                        rejectfunc
                        break;
                }
            })
        }
    }
    window.customePromise = customePromise;
})();
posted @ 2019-07-11 14:48  chok  阅读(556)  评论(0编辑  收藏  举报