• 简易版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;
    })();
  • 相关阅读:
    selector在手机上或浏览器显示各种姿势(虚拟下拉菜单)
    关于JavaScript禁止点击事件
    设为主页以及其它功能实现
    判断浏览器是否支持flash
    渐进式增强
    判断用户Input输入的事件来进行登陆
    移动端底部input被弹出的键盘遮挡
    关于中间文字实现
    关于小程序navigator没有高的情况
    M.2接口NVMe协议的固态硬盘读写速度是SATA接口的两倍
  • 原文地址:https://www.cnblogs.com/shentao11023/p/11169942.html
Copyright © 2020-2023  润新知