promise–手撕代码,彻底搞懂promise

前言:

Promise是es6的新特性之一,在实际开发中被广泛应用,它也是在面试中经常被问到的问题,比如:利用promise考察事件循环机制、promise的几种状态、怎么使用promise处理异步等等。由promise引出来的问题很多,解决这些问题的最好方法不是case by case,而是深入了解promise,万变不离其踪。彻底搞懂promsie,这些问题都影刃而解。

手写实现Promise

先看一下promise的简单使用示例:

    let p=new Promise((resolve,reject)=>{
        console.log('初始化');
        resolve('success');
    })
    .then((res,rej)=>{
        console.log('then执行',res)
     })
     输出结果:
     - 初始化
     - then执行 success
复制代码

从易到难,我们先简单实现,然后一点点补充改善。首先我们实现resolve和reject功能,从上面?栗子我们需要注意一下几点:

  1. promise有三种状态,分别是PENDING,FULFILLED,REJECTED;
  2. promise初始化时,会立即执行传入的函数,执行函数中会改变promise的状态—>输出了‘初始化’,并改变了Promise的状态;
  3. 从PENDING可以到FULFILLED状态或者REJECTED状态,但是状态一旦改变后,就不可以再改变;
  4. 状态改变后,通过then方法执行相应的回调—>promise执行了resolve函数,状态为FULFILLED,并把值传给了then中的res参数;

(1)基于以上四点信息,我们其实就能够写出第一版代码:

class HD{
    //定义三种状态
    static PENDING="pending";
    static FULFILLED="fulfiled";
    static REJECTED="rejected";
    //类执行函数,实例化的时候自动执行
    constructor (executor){
        //定义初始值为undefined,后面用来保存resolve,reject传进的参数
        this.value=undefined;
        //定义初始状态为pending
        this.status=HD.PENDING;
        //加try...catch,防止在执行executor时出错,比如console.log(a),a不存在报错,则执行rejected函数
        try {
            executor(this.resolve,this.rejected);
        } catch (error) {
            this.rejected(error)
        }
    }
    //定义resolve函数
    resolve=(value)=>{
        //只有状态为pending,才可以转为其他状态
        if(this.status == HD.PENDING){
            //改变promsie状态
            this.status=HD.FULFILLED;
            //this.value记录resolve传入的值,供then函数使用
            this.value=value;  
        } 
    }
    //同上
    rejected=(reason)=>{
        if(this.status == HD.PENDING){
            this.status=HD.REJECTED;
            this.value=reason;  
        } 
    }
    then=(onFulfilled,onRejected)=>{
        //根据不同状态,在then中执行不同的函数
        if(this.status==HD.FULFILLED){
            let result=onFulfilled(this.value);
        };
        if(this.status==HD.REJECTED){
            let result=onRejected(this.value);
        };
    }
}

let p=new HD((res,rej)=>{
        console.log('初始化');
        res('success');
    })
    .then((value)=>{
        console.log(value)
    })
    输出:
    -  初始化
    -  success
复制代码

(2)excutor中异步改变状态

上面的代码隐藏着一个问题,比如 :

    let p=new HD((res,rej)=>{
        setTimeout(()=>{
            console.log('初始化');
            res('success');
        })
    })
    .then((value)=>{
        console.log(value)
    })
复制代码

只会输出‘初始化’,不会输出success。因为setTimeout是异步任务,当执行then时,promise的状态还没有改变,仍是pending状态,因此我们需要对上面代码继续改造:

class HD{
    //定义三种状态
    static PENDING="pending";
    static FULFILLED="fulfiled";
    static REJECTED="rejected";
    //定义一个空数组,存放then执行的回调函数,防止executor异步改变status值
    this.callbacks=[];
    //类执行函数,实例化的时候自动执行
    constructor (executor){
        //定义初始值为undefined,后面用来保存resolve,reject传进的参数
        this.value=undefined;
        //定义初始状态为pending
        this.status=HD.PENDING;
        //加try...catch,防止在执行executor时出错,比如console.log(a),a不存在报错,则执行rejected函数
        try {
            executor(this.resolve,this.rejected);
        } catch (error) {
            this.rejected(error)
        }
    }
    //定义resolve函数
    resolve=(value)=>{
        //只有状态为pending,才可以转为其他状态
        if(this.status == HD.PENDING){
            //改变promsie状态
            this.status=HD.FULFILLED;
            //this.value记录resolve传入的值,供then函数使用
            this.value=value;  
            //执行callbacks里的fulfilled函数,当resolve是异步改变状态时,确保then回调可以执行
            this.callbacks.map((callback)=>{
               callback.onFulfilled(this.value)
            })
        } 
    }
    //同上
    rejected=(reason)=>{
        if(this.status == HD.PENDING){
            this.status=HD.REJECTED;
            this.value=reason;  
            this.callbacks.map((callback)=>{
               callback.onRejected(this.value)
            })
        } 
    }
    then=(onFulfilled,onRejected)=>{
        //如果是pending状态,把then回调函数存入到callbacks
        if(this.status==HD.PENDING){
            this.callbacks.push({
                onFulfilled:value=>{onFulfilled(value)},
                onRejected:value=>{onRejected(value)}
            })
        };
        //根据不同状态,在then中执行不同的函数
        if(this.status==HD.FULFILLED){
            let result=onFulfilled(this.value);
        };
        if(this.status==HD.REJECTED){
            let result=onRejected(this.value);
        };
    }
}

let p=new HD((res,rej)=>{
        console.log('初始化');
        res('success');
    })
    .then((value)=>{
        console.log(value)
    })
    输出:
    -  初始化
    -  success
    这样就可以解决excutor中异步改变状态时,then回调执行问题
    
复制代码

(3)then的链式调用
我们使用promise,最想使用的功能应该就是then的链式调用,很明显,then返回的应该也是一个promsie对象,这样才能继续调用then方法,从而实现then的链式调用。我们对上面代码继续进行改造:

        class HD{
        //定义三种状态
        static PENDING="pending";
        static FULFILLED="fulfiled";
        static REJECTED="rejected";
        
        //类执行函数,实例化的时候自动执行
        constructor (executor){
            this.callbacks=[];
            //定义初始值为undefined,后面用来保存resolve,reject传进的参数
            this.value=undefined;
            //定义初始状态为pending
            this.status=HD.PENDING;
            //加try...catch,防止在执行executor时出错,比如console.log(a),a不存在报错,则执行rejected函数
            try {
                executor(this.resolve,this.rejected);
            } catch (error) {
                this.rejected(error)
            }
        }
        //定义resolve函数
        resolve=(value)=>{
            //只有状态为pending,才可以转为其他状态
            if(this.status == HD.PENDING){
                //改变promsie状态
                this.status=HD.FULFILLED;
                //this.value记录resolve传入的值,供then函数使用
                this.value=value; 
                this.callbacks.map((callback)=>{
                    callback.onFulfilled(this.value)
                })
            } 
        }
        //同上
        rejected=(reason)=>{
            if(this.status == HD.PENDING){
                this.status=HD.REJECTED;
                this.value=reason;  
                this.callbacks.map((callback)=>{
                    callback.onRejected(this.value)
                })
            } 
        }
        then=(onFulfilled,onRejected)=>{
            var promise=new HD((resolve,reject)=>{
                //根据不同状态,在then中执行不同的函数
                if(this.status==HD.FULFILLED){
                    try {
                        let result=onFulfilled(this.value)
                        //then返回promise的处理
                        if(result instanceof HD){
                            //如果onFulfilled返回的是一个promise对象,要进一步处理,不能直接把promise返回
                            // result.then(value=>{
                            //     //如果then中返回的result是promise,调then函数,把其中值返回出来,经过resolve(value),使得then返回的值是result的resolve值
                            //     resolve(value)
                            // },reason=>{
                            //     reject(reason)
                            // })
                            //这一句是上一段的简化
                            result.then(resolve,reject);

                        }else{
                            //如果返回的不是promise对象,直接返回
                            resolve(result)
                        }
                    } catch (error) {
                        reject(error)
                    }
                };
                if(this.status==HD.REJECTED){
                    try {
                        let result=onRejected(this.value)
                        if(result instanceof HD){
                            // resolve,reject是父级的
                            result.then(resolve,reject);
                        }else{
                            resolve(result)
                        }
                    } catch (error) {
                        reject(error)
                    }
                };
                if(this.status==HD.PENDING){
                    this.callbacks.push({
                        onFulfilled:value=>{
                            try {
                                let result=onFulfilled(value)
                                if(result instanceof HD){
                                    //使用父级的resolve,reject从而改变父级的value和状态
                                    result.then(resolve,reject);
                                }else{
                                    resolve(result)
                                }
                            } catch (error) {
                                //发生异常说明上个promise对像有错误,那就用下个Promise的reject处理,状态变为rejected
                                reject(error)
                            }
                        },
                        onRejected:value=>{
                            try {
                                let result=onRejected(value)
                                // resolve(result)
                                //处理then返回promise
                                if(result instanceof HD){
                                    result.then(resolve,reject);
                                }else{
                                    resolve(result)
                                }
                            } catch (error) {
                                // onRejected(error)
                                reject(error)
                            }
                        }
                    })
                }
            })
            return promise
        };
    }

let p=new HD((resolve,reject)=>{
            setTimeout(()=>{
                resolve('success1')
            },1000)
        })
        .then(value=>{
            return new HD((resolve,reject)=>{
                reject('reject')
            })
        },reason=>{
            return new HD((resolve,reject)=>{
                reject('reject2')
            })
        })
        .then(value=>{
            console.log('value',value)
        },reason=>{
            console.log('reason',reason)
        })
   输出:
   - reason reject
复制代码

上面改造主要包括两个方面:

  1. then方法返回一个promise对象;
  2. then执行回调后如果返回的仍是一个Promise对象,继续对该promise对象进行then的调用,使得then的返回不能是promsie对象的嵌套;

(4)实现Promise.resolve,Promise.reject
对于Promise.resolve().then()的形式,可以看出实现Promise.resolve返回的也是一个Promise对象,并且Promise的状态应该变为resolve。Promise.reject同理。因此我们可以写出这两个函数,在上面的代码中加入两个静态方法:

        static resolve=(value)=>{
            return new HD((resolve,rejected)=>{
                resolve(value)
            })
        }
        static reject=(value)=>{
            return new HD((resolve,reject)=>{
                reject(value)
            })
        }
        
       HD.resolve('success111')
        .then((res)=>{
            console.log(res)
        });
       输出:
       - success111
复制代码

(5)实现Promise.all()方法
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
添加静态all方法:

     static all=(promises)=>{
            let value=[];
            return new HD((resolve,rejected)=>{
                promises.map((promise)=>{
                    promise.then((res)=>{
                        value.push(res);
                        if(value.length==promises.length){
                            resolve(value)
                        }
                        },(rej)=>{
                            rejected(rej)
                    })
                })
            })
        }
        
        let p=new HD((resolve,reject)=>{
        resolve('4')
    });
    let m=new HD((resolve,reject)=>{
        resolve('5')
    });
    HD.all([p,m]).then((val)=>{console.log(val)},(res)=>{console.log(res)})
    输出:
    - ['4','5']
复制代码

all方法实现的思路很简单,就是把输入的promise数组进行循环遍历,分别执行其then函数,如果有一个promise函数执行了rejected函数,那么all返回的promise状态就为rejected,并且返回失败的结果,其他Promise暂停执行。如果所有传入的promise都是resolve状态,那么用value保存每一个promsie的返回值,当所有promsie执行完后,最后返回最终结果。

(6)实现Promsie.race方法
Promsie.race方法要比Promise.all()方法实现起来简单,顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
添加静态race方法:

     static race=(promises)=>{
            return new HD((resolve,rejected)=>{
                promises.map((promise)=>{
                    promise.then(res=>{
                        resolve(res)
                    },rej=>{
                        rejected(rej)
                    })
                })
            })
        }
        
    let p=new HD((resolve,reject)=>{
        setTimeout(() => {
            resolve('1')
        },2000);
    });
    let m=new HD((resolve,reject)=>{
        setTimeout(() => {
            resolve('2')
        }, 1000);
    });
    HD.race([p,m]).then((val)=>{console.log(val)},(res)=>{console.log(res)})
    一秒后输出:2
复制代码

race实现思路很简单,一旦有promise执行then回调,就立马调用resolve或者rejected从而改变HD.race返回的promsie的状态,从而实现‘竞争执行’的效果。

总结

以上代码实现了一个promsie函数,支持异步调用、then的链式回调、resolve、rejected、all、race等功能。通过手写Promise这一个过程,让我们对promise有更加深入的理解。如果代码中有不理解、不足或者不对的地方,欢迎大家在留言区评论,一起加油?。谢谢。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享