手写Promise完整版+静态方法all和race

13 阅读4分钟

手写Promise完整版+静态方法all和race

手写Promise是面试常考考点,个人认为背后主要考察的点是对于上下文环境this和作用域(闭包)的深刻理解(还有一个很重要的点bind,apply,call强制改变this指向并且使函数this不再由运行时决定),才能判断出某个位置的this是什么,某个函数可否实现普通函数和箭头函数的替换...等等

class myPromise{
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';

    constructor(executor_function){//重要:constructor中的this指向的一定是新建的实例
        this.state = myPromise.PENDING;
        this.value = null;
        this.callbackArr=[];
        try{
            executor_function(this.resolve.bind(this) , this.reject.bind(this));//牢记:以回调形式使用函数作为参数的时候,这个函数内部的上下文this默认为module.export()/window,所以一定要显示绑定this
        }catch(err){
            this.reject(err);
        }
    }

    resolve(value){
        if(this.state === myPromise.PENDING){
            this.state = myPromise.FULFILLED;
            this.value = value;
            setTimeout(()=>{
                this.callbackArr.map(funcItem=>{
                    funcItem.onFulfilledFunc(this.value);
                })
            })
        }
    }

    reject(error){
        if(this.state === myPromise.PENDING){
            this.state = myPromise.REJECTED;
            this.value = error;
            setTimeout(()=>{
                this.callbackArr.map(funcItem=>{
                    funcItem.onRejectedFunc(this.value)
                })
            })
        }
    }

    then(onFulfilled,onRejected){
        //这个then函数的上下文this,仍然是“调用者”promise,因为调用情况是promise.then(),所以then函数作用域中的this只能是上一个promise实例
        if(typeof onFulfilled !=='function'){
            onFulfilled = function(value){
                return value;
            }
        }
        if(typeof onRejected !=='function'){
            onRejected = function(error){
                return error;
            }
        }
        
        let thenPromise = new myPromise((res,rej)=>{
            if(this.state === myPromise.PENDING){
                //如果上一个promise还未被解决,需要把onFulfilled和onRejected挂起,等待上一个promise状态变为解决的时候,作为回调函数执行
                this.callbackArr.push({
                    onFulfilledFunc:(value)=>{
                        this.parse(thenPromise,onFulfilled(value),res,rej);
                    },
                    onRejectedFunc:(err)=>{
                        this.parse(thenPromise,onRejected(err),res,rej);
                    }
                })
            }
            if(this.state === myPromise.FULFILLED){
                //如果上一个promise状态变成了fulfilled,then中就执行onFulfilled函数
                setTimeout(()=>{this.parse(thenPromise,onFulfilled(this.value),res,rej)});
            }
            if(this.state === myPromise.REJECTED){
                //如果上一个promise状态变成了rejected,then中就执行onRejected函数
                setTimeout(()=>{this.parse(thenPromise,onRejected(this.value),res.rej)});
            }
        })
        return thenPromise;
    }

     catch(onRejected){
        if(typeof onRejected !=='function'){
            onRejected = (onRejected)=>{
                return onRejected;
            }
        }
        let catchPromise =  new myPromise((res,rej)=>{
           if(this.state=myPromise.REJECTED){
                setTimeout(()=>{this.parse(thenPromise,onRejected(this.value),res.rej)});
           }
           if(this.state=myPromise.FULFILLED){
                setTimeout(()=>{this.parse(thenPromise,onFulfilled(this.value),res,rej)});
           }
           if(this.state=myPromise.PENDING){
                this.callbackArr.push({
                    onFulfilledFunc:(value)=>{
                        this.parse(catchPromise,onFulfilled(value),res,rej);
                    },
                    onRejectedFunc:(value)=>{
                        this.parse(catchPromise,onRejected(value),res,rej);
                    }
                })
           }
        })
        return catchPromise
    }

    parse(promise,result,resolve,reject){   
        if(promise === result){
            throw new Error('then返回的Promise不能和onFulfilled产生的this一样');
        }
        try{
            if(result instanceof myPromise){
                return result.then(resolve,reject);
            }else{
                return resolve(result);
            }
        }catch(err){
            return reject(err);
        }
    }

    static resolve(value){
        return new myPromise((res,rej)=>{
            if(value instanceof myPromise){
                value.then(res,rej);
            }else{
                res(value);
            }
        })
    }

    static reject(err){
        return new myPromise((res,rej)=>{
            rej(err);
        })
    }

    static all(promiseArr){
        let resArr=[];
        let promiseLen = promiseArr.length;
        let fulfilledLen = 0;
        return new myPromise((res,rej)=>{
            for(let i=0;i<promiseLen;i++){
                let promiseNow = promiseArr[i];
                myPromise.resolve(promiseNow).then((value)=>{
                    fulfilledLen++;
                    resArr[i] = value;
                    //我感觉这里很关键,在then()这个事件循环微任务中判断promiseArr数组是否都获得结果并存入resArr里,而不是在同步代码for循环中判断。
                    if(fulfilledLen===promiseLen) res(resArr);
                    //这里还体现了块级作用域和闭包的知识点,因为for内是块级作用域且变量i是用let声明的,所以then的参数(回调函数)才能记住此时的i是多少,能看到常考的同步代码先于异步代码执行,导致同步代码中i变量若不使用闭包,将会被覆盖,导致异步代码中i变化的知识点。如果一定要用var声明i变量(函数作用域),则需要构建立即执行函数(IIFE)来产生基于函数作用域的闭包
                })
                .catch((err)=>{
                    rej(err);
                })
            }
        })
    }

    static race(promiseArr){
        return new myPromise((res,rej)=>{
            for(let i=0;i<promiseArr.length;i++){
                let promiseNow = promiseArr[i];
                myPromise.resolve(promiseNow).then((value)=>{
                    res(value)
                })
                .catch((err)=>{
                    rej(err);
                })
            }
        })
    }
}

同时,Promise.then()中的回调函数会被作为微任务处理(异步处理)的理解也很必要,以下用一段代码给出案例,并用手写的myPromise代码的关联部分给出解释

Promise.resolve(8).then(res=>{console.log(res);return Promise.resolve(9);}).then(res=>console.log(res,'1'));
Promise.resolve(5).then(res=>{console.log(res);return 10;}).then(res=>console.log(res,'2'));
/*
结果:
8
5
10 2
9 1
*/

8 和 5 的顺序很好理解,他们都位于两个Promise链的第一个then回调中,因此先打印8,后打印5,但是为什么接下来的第二个then调用中,先打印10 2,再打印9 1呢?

原因是then方法中,如果onFulfilled(正确时的回调函数)返回了Promise类型时,then返回的Promise需要再“等待”一个Promise解析成then的时间,就算这个Promise解析是同步的,但是调用then就属于异步操作(then中回调都用setTimeout包裹),所以第二个Promise链的最后一个then要多等一次事件循环才能执行。

    parse(promise,result,resolve,reject){   
        if(promise === result){
            throw new Error('then返回的Promise不能和onFulfilled产生的this一样');
        }
        try{
            if(result instanceof myPromise){
                return result.then(resolve,reject);//注意这里,相当于在Promise链上多加了一个then方法,而每执行一个then方法就要调用一次setTimeout,parse本身就是在setTimeout的回调中执行的
            }else{
                return resolve(result);
            }
        }catch(err){
            return reject(err);
        }
    }