手写Promise太难?那是你不会拆分需求!

手写Promise太难?那是你不会拆分需求!

现在面试也越来越内卷了,动不动就手写各种源码。当然手写源码不是目的,可能就是为了考察对知识的掌握程度。很多人一听到手写Promise就头大,毫无思路,其实我们把Promise功能需求拆封一下,分步骤实现;写起来就相对简单了,并且易于理解,下面就带大家手写一个Promise

:sunny:阅读小提示:本文看起来篇幅很长,因为每部分都贴出了完整代码,所以代码量比较大。每个步骤新增/修改的代码部分都会标明步骤和思路,跟着步骤阅读即可。耐心看完,相信会有收获!

Promise基本逻辑实现

  • 先贴代码,分析一下:
    new Promise((resolve,reject)=>{
        console.log('LonJIn带你手写Promise');
        resolve('hello')
    }).then((res)=>{
        console.log(res)
    })

核心逻辑分析

  • 首先,我们看到Promise前面调用了new,说明Promise是一个类

  • 它有三种状态:等待(pending)成功(fulfilled)失败(rejected),状态一旦从pending变成fulfilledrejected就不可更改

  • new Promise中传入了一个函数,我们可以把它称之为执行器,执行器会立即执行

  • 在执行器中传入了resolvereject,用来改变它的状态

  • then方法内部做的事情就是判断状态,如果状态是成功,调用成功回调函数,如果状态是失败,就调用失败回调函数,且then方法会接收一个成功或者失败的值。

代码实现

  • 我们先建立一个MyPromise.js文件,在这个文件中编写我们的Promise。
    //定义三个常量
    const PENDING='pending', //等待
        FULFILLED='fulfilled', //成功
        REJECTED='rejected'; //失败

    class MyPromise{
        constructor(exector){
            // 立即执行函数,传入resolve方法和reject方法
            exector(this.resolve,this.reject)
        };

        //定义一个初始状态
        status=PENDING;

        //保存成功后的值
        value=undefined;

        //保存失败后的值
        reason=undefined;

        //成功
        resolve=value=>{
            //判断当前状态是否为PENDING,如果不是就return
            if(this.status!==PENDING)return;
            //更改状态为fulfilled
            this.status=FULFILLED;
            //保存成功的返回值
            this.value=value;

        };
        //失败
        reject=reason=>{
            //判断当前状态是否为PENDING,如果不是就return
            if(this.status!==PENDING)return;
            //更改状态为rejected
            this.status=REJECTED;
            //保存失败的返回值
            this.reason=reason;
        };

        //then方法会接收两个回调函数,分别为成功和失败的回调函数
        then(successCallback,failCallback){
            //判断当前状态,然后调用相应的函数
            if(this.status===FULFILLED){
                //传入返回值
                successCallback(this.value)
            }else if(this.status===REJECTED){
                //传入返回值
                failCallback(this.reason)
            }
        }
    }
    //导出MyPromise
    module.exports = MyPromise
  • 我们上面分析的基本完成,新建一个index.js导入测试一下:
    //index.js
    const MyPromise=require('./MyPromise');

    let n=new MyPromise((resolve,reject)=>{
        console.log('lonjin');
        resolve(200)
        // reject('erro')
    })

    n.then((val)=>{
        console.log(val) 
    },(erro)=>{
        console.log(erro)
    })
    //lonjin
    //200
  • 调用之后可以正常输出,说明之前写的没有问题,但还有个问题:我们没有考虑异步的时候该怎么办?比如下面的代码:
    //index.js
    const MyPromise=require('./MyPromise');

    let n=new MyPromise((resolve,reject)=>{
        //由于setTimeout为异步代码,then会马上执行,但此时状态还为pending之前并没有判断等待这个状态
        setTimeout(() => {
            resolve(200)
        }, 100);
    })

    n.then((val)=>{
        console.log(val) 
    },(erro)=>{
        console.log(erro)
    })

处理异步调用

  • 由于执行器可能有异步代码,所以我们在then方法中还需要判断一下状态,然后做相应的处理

代码如下(为了思路清晰,只对做更改的地方加注释,会标注实现步骤,按步骤阅读即可):

    const PENDING='pending',
        FULFILLED='fulfilled',
        REJECTED='rejected';

    class MyPromise{
        constructor(exector){
            exector(this.resolve,this.reject)
        };

        status=PENDING;

        value=undefined;

        reason=undefined;

        //定义一个成功回调函数
        successCallback=undefined;
        //定义一个失败回调函数
        failCallback=undefined;

        resolve=value=>{
           
            if(this.status!==PENDING)return;
           
            this.status=FULFILLED;

            this.value=value;
                /* 步骤2
                -------------------------------------------------
                判断成功回调是否存在,如果存在,直接调用
                */
            this.successCallback && this.successCallback(this.value)
        };
       
        reject=reason=>{
          
            if(this.status!==PENDING)return;
          
            this.status=REJECTED;
            
            this.reason=reason;

            /* 步骤3
                -------------------------------------------------
                判断失败回调是否存在,如果存在,直接调用
            */
            this.failCallback && this.failCallback(this.reason)
        };

       
        then(successCallback,failCallback){
            
            if(this.status===FULFILLED){  
               
                successCallback(this.value)
                
            }else if(this.status===REJECTED){ 
               
                failCallback(this.reason)

            }else{
                /* 步骤1
                -------------------------------------------------
                等待状态,处理异步逻辑
                由于不知道状态,所以我们需要把成功和失败的回调函数先保存起来
                保存回调函数 */
                this.successCallback=successCallback;
                this.failCallback=failCallback;
                
            }
        }
    }
    //导出MyPromise
    module.exports = MyPromise
  • 验证代码:
    //index.js
    const MyPromise=require('./MyPromise');

    let n=new MyPromise((resolve,reject)=>{
        //由于setTimeout为异步代码,then会马上执行,但此时状态还为pending之前并没有判断等待这个状态
        setTimeout(() => {
            resolve(200)
        }, 100);
    })

    n.then((val)=>{
        console.log(val) 
    },(erro)=>{
        console.log(erro)
    })
  • 我们可以看到代码可以正常输出,异步就算处理完成。

实现then()多次调用

  • Promisethen方法是可以被多次调用的。如果是同步回调,那么直接返回当前的值就行;如果是异步回调,那么保存的成功失败的回调,需要用不同的值保存,因为都互不相同。所以之前的代码需要改造一下。
     //index.js
    const MyPromise=require('./MyPromise');

    let n=new MyPromise((resolve,reject)=>{
        setTimeout(() => {
            resolve(200)
        }, 100);
    })
    //多次调用的情况 目前只会输出一次
    n.then((val)=>{
        console.log(val) 
    },(erro)=>{
        console.log(erro)
    })

    n.then((val)=>{
        console.log(val) 
    },(erro)=>{
        console.log(erro)
    })

    n.then((val)=>{
        console.log(val) 
    },(erro)=>{
        console.log(erro)
    })

代码实现

  • 为了解决多次调用的情况,之前我们定义了一个successCallback回调函数,默认为undefined,这时候我们需要把它改成数组形式,在resolvereject回调函数中取数组最后一个保存的回调执行即可

    const PENDING='pending',
          FULFILLED='fulfilled', 
          REJECTED='rejected';

    class MyPromise{
        constructor(exector){
            exector(this.resolve,this.reject)
        };

        status=PENDING;
        value=undefined;
        reason=undefined;

        /* 步骤1 
            -------------------------------------
            将successCallback和failCallback改成数组
        */
        successCallback=[];
    
        failCallback=[];

        resolve=value=>{
            if(this.status!==PENDING)return;
        
            this.status=FULFILLED;

            this.value=value;

            /* 步骤3 
                -------------------------------------
                判断successCallback数组中是否有值,有值的话就取数组最后的执行
            */
            while(this.successCallback.length)this.successCallback.shift()(this.value)
        };
    
        reject=reason=>{
        
            if(this.status!==PENDING)return;
            
            this.status=REJECTED;
        
            this.reason=reason;
            /* 步骤4
                -------------------------------------
                判断failCallback数组中是否有值,有值的话就取数组最后的执行
            */
            while(this.failCallback.length)this.failCallback && this.failCallback.shift()(this.reason)
        };

        then(successCallback,failCallback){
            if(this.status===FULFILLED){ 

                successCallback(this.value)
                
            }else if(this.status===REJECTED){
               
                failCallback(this.reason)

            }else{ 
                /* 步骤2
                    -------------------------------------
                    将successCallback和failCallback函数push到数组中
                */
                this.successCallback.push(successCallback);
                this.failCallback.push(failCallback);

            }
        }

       
    }
    module.exports = MyPromise
  • 验证代码:
    //index.js
    const MyPromise=require('./MyPromise');

    let n=new MyPromise((resolve,reject)=>{
        setTimeout(() => {
            resolve(200)
        }, 500);
    })

    n.then((val)=>{
        console.log('one-'+val)
    },(erro)=>{
        console.log(erro)
    })

    n.then((val)=>{
        console.log('two-'+val)
    },(erro)=>{
        console.log(erro)
    })

    n.then((val)=>{
        console.log('three-'+val)
    },(erro)=>{
        console.log(erro)
    })
    /*
    one-200
    two-200
    three-200
    */

代码正常输出,证明无问题。

实现then方法的链式调用

还是先理一下需求,再进行开发:

  • then方法的链式调用会返回一个Promise,且then方法的return值会作为参数传给下一个then

代码实现

   
    const PENDING='pending',
          FULFILLED='fulfilled', 
          REJECTED='rejected';

    class MyPromise{
        constructor(exector){
            exector(this.resolve,this.reject)
        };

        status=PENDING;
        value=undefined;
        reason=undefined;

        successCallback=[];
        failCallback=[];

        resolve=value=>{
            if(this.status!==PENDING)return;

            this.status=FULFILLED;
          
            this.value=value;
          
            while(this.successCallback.length)this.successCallback.shift()(this.value)
        };

        reject=reason=>{
            if(this.status!==PENDING)return;

            this.status=REJECTED;
           
            this.reason=reason;
          
            while(this.failCallback.length)this.failCallback && this.failCallback.shift()(this.reason)
        };


        then(successCallback,failCallback){

            /* 步骤1 
            ------------------------
            1.then方法会返回一个Promise,所以这里需要创建一个Promise,然后return回去
            2. 我们先把 成功回调函数保存为n
            3.然后在MyPromise类的外部再写一个resolvePromise做处理
            */
            let prmoise2=new MyPromise((resolve,reject)=>{
               
                if(this.status===FULFILLED){
                    //保存成功回调函数
                    let n=successCallback(this.value)
                    //传入resolvePromise函数中去做判断
                    resolvePromise(n,resolve,reject)
                }else if(this.status===REJECTED){ 
                    
                    failCallback(this.reason)
                }else{ 
                    this.successCallback.push(successCallback);
                    this.failCallback.push(failCallback); 
                }
            });
            return prmoise2;
            
        }
    }
    /* 步骤2
    ---------------------------
      //判断x是不是其实例的对象
    */
    function resolvePromise(n,resolve,reject){
        // 判断x是不是其实例对象,如果是就直接调用then方法
        if(n instanceof MyPromise){
            n.then(resolve,reject)
        }else{
            //普通值,直接调用resolve
            resolve(n)
        }
    }
    module.exports = MyPromise

:rocket:验证代码

    const MyPromise=require('./MyPromise');

    let n=new MyPromise((resolve,reject)=>{
        resolve('lonjin')
    })

    function m(){
        return new MyPromise((resolve,reject)=>{
                console.log('------')
                resolve('hello')
            })
    }
    n.then((res)=>{
        console.log(res)
        return m();
    })
    .then((res)=>{
        console.log(res)
    })
    /*
    lonjin
    ------
    hello
    */

:warning:特殊情况处理

:one: 如果then方法返回的是自己的promise对象,则会发生promise的嵌套,这个时候程序会报错,如下代码:

    var promise = new Promise((resolve, reject) => {
         resolve(100)
    })
    var n = promise.then(value => {
        console.log(value)
        return n
    })
// 100
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

:two: 同时还要处理一种情况,如果我们在new MyPromise中传入的立即执行器中代码报错,以及then()中写的代码报错,我们都需要捕获到,然后执行reject

:three: 之前一直是处理resolve的情况,还需要处理一下reject的情况

这时候我们需要改造一下刚才写的代码:

    const PENDING='pending', 
          FULFILLED='fulfilled', 
          REJECTED='rejected';

    class MyPromise{
        /* 步骤1
        -------------------
        捕获一下立即执行器中的错误
        */
        constructor(exector){
          try{
                exector(this.resolve,this.reject)
          }catch(e){
              reject(e)
          }
        };

        status=PENDING;

        value=undefined;

        reason=undefined;

        successCallback=[];

        failCallback=[];

        resolve=value=>{
         
            if(this.status!==PENDING)return;
         
            this.status=FULFILLED;
           
            this.value=value;
            /* 步骤6
            -------------
            由于修改了下面的successCallback,所以不需要传递this.value
            */
            while(this.successCallback.length)this.successCallback.shift()()
        };

        reject=reason=>{
          
            if(this.status!==PENDING)return;
          
            this.status=REJECTED;
          
            this.reason=reason;

             /* 步骤7
            -------------
            由于修改了下面的failCallback,所以不需要传递this.reason
            */
            while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
        };

       
        then(successCallback,failCallback){

            let prmoise2=new MyPromise((resolve,reject)=>{
              
                if(this.status===FULFILLED){ 
                    
                    /* 步骤2
                    ---------------------
                    因为new Promise需要执行完成之后才有promise2,同步代码中没有pormise2,
                    所以这部分代码需要异步执行 ,我们把prmoise2传入resolvePromise函数中
                    去判断一下prmoise2是否等于n,同时需要捕获一下错误
                    */
                    setTimeout(() => {
                        try{
                             let n=successCallback(this.value)
                             resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }else if(this.status===REJECTED){ 
                    /* 
                    步骤3
                    ---------------------
                    处理一下失败的情况
                    */
                    setTimeout(() => {
                        try{
                             let n= failCallback(this.reason)
                             resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                   
                }else{ 
                     /* 
                    步骤5
                    ---------------------
                    修改一下异步情况
                    */
                    this.successCallback.push(()=>{
                        setTimeout(() => {
                            try{
                                let n=successCallback(this.value)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    });
                    this.failCallback.push(()=>{
                        setTimeout(() => {
                            try{
                                let n= failCallback(this.reason)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    }); 
                }
            });
            return prmoise2;
            
        }
    }

    function resolvePromise(prmoise2,n,resolve,reject){
        /* 步骤4 
        ----------------
        如果n和prmoise2相等 则返回错误提示
        */
        if(prmoise2===n){
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        // 判断x是不是其实例对象
        if(n instanceof MyPromise){
            n.then(resolve,reject)
        }else{
            //普通值,直接调用resolve
            resolve(n)
        }
    }

    //导出MyPromise
    module.exports = MyPromise

:rocket:验证代码

    //验证1
    //index.js
    const MyPromise = require('./myPromise')

    var promise = new MyPromise((resolve, reject) => {
        resolve(100)
    })
    var p1 = promise.then(value => {
        console.log(value)
        return p1
    })
    
    p1.then((val)=>{
        console.log(val)
    },(erro)=>{
        console.log(erro.message)
    })
    // 100
    //  Chaining cycle detected for promise #<Promise>

    //验证2
    var errofn = new MyPromise((resolve, reject) => {
    
        throw new Error('执行器抛出异常!')
        
    })
    errofn.then((val)=>{
        console.log(val)
    },(erro)=>{
        console.log(erro)
    })
    // 执行器错误

将then方法的参数变成可选参数

  • 我们知道,调用then()时候也可以不传递任何参数,如下面这种情况
    let promise =new Promise((resolve,reject)=>{
    resolve('lonjin')
    })

    promise.then().then().then((value)=>{
    console.log(value)
    })
    //lonjin
  • 相当于这样:
    promise
    .then(value => value)
    .then(value => value)
    .then(value => value)
    .then(value => console.log(value))

所以我们要把then方法的参数改为可选参数,如果有参数就传入,如果没有返回value:

代码实现

    const PENDING='pending', 
          FULFILLED='fulfilled', 
          REJECTED='rejected';

    class MyPromise{
        constructor(exector){
          try{
            exector(this.resolve,this.reject)
          }catch(e){
              reject(e)
          }
        };

        status=PENDING;

        value=undefined;
        reason=undefined;

        successCallback=[];
        failCallback=[];

        resolve=value=>{
            if(this.status!==PENDING)return;
            this.status=FULFILLED;
            this.value=value;
            while(this.successCallback.length)this.successCallback.shift()()
        };

        reject=reason=>{
            if(this.status!==PENDING)return;
            this.status=REJECTED;
            this.reason=reason;
            while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
        };

        then(successCallback,failCallback){

            /*步骤1
            ----------------
            判断参数是否存在
            */
           successCallback=successCallback?successCallback:value=>value;
           failCallback=failCallback?failCallback:reason=>{throw reason};

            let prmoise2=new MyPromise((resolve,reject)=>{
                if(this.status===FULFILLED){ 
                    setTimeout(() => {
                        try{
                             let n=successCallback(this.value)
                             resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }else if(this.status===REJECTED){ 
                    setTimeout(() => {
                        try{
                             let n= failCallback(this.reason)
                             resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                   
                }else{ 
                    this.successCallback.push(()=>{
                        setTimeout(() => {
                            try{
                                let n=successCallback(this.value)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    });
                    this.failCallback.push(()=>{
                        setTimeout(() => {
                            try{
                                let n= failCallback(this.reason)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    }); 
                }
            });
            return prmoise2;
            
        }
    }

    function resolvePromise(prmoise2,n,resolve,reject){
        if(prmoise2===n){
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        if(n instanceof MyPromise){
            n.then(resolve,reject)
        }else{
            resolve(n)
        }
    }

    module.exports = MyPromise

:rocket:验证代码

    const MyPromise = require('./myPromise')
        let promise =new MyPromise((resolve,reject)=>{
        resolve('lonjin')
    })

    promise.then().then().then((value)=>{
     console.log(value)
    })
    //lonjin

Promise.all()方法实现

还是先分析一下all方法:

    function fn1(){
        return new Promise((resolve,reject)=>{
            setTimeout(() => {
            resolve('fn1')
            }, 3000);
        })
    }

    function fn2(){
        return new Promise((resolve,reject)=>{
            resolve('fn2')
        })
    }

    Promise.all(['a','b',fn1(),fn2(),'e'])
    .then((res)=>{
         console.log(res)
    },(erro)=>{
        console.log(erro)
    })
    //[ 'a', 'b', 'fn1', 'fn2', 'e' ]
  • all方法接收一个数组,其内部可以传入普通值Promise对象

  • all方法返回的也是一个Promise对象,其返回值为一个数组

  • all方法中传入的Promise对象,如果都是成功,返回为成功,如果有一个失败,就会走失败的回调函数

分析完代码,我们就可以进行编写了,具体代码如下:

代码实现

    const PENDING='pending', 
          FULFILLED='fulfilled', 
          REJECTED='rejected';

    class MyPromise{
        constructor(exector){
          try{
            exector(this.resolve,this.reject)
          }catch(e){
              reject(e)
          }
        };

        status=PENDING;

        value=undefined;
        reason=undefined;

        successCallback=[];
        failCallback=[];

        resolve=value=>{
            if(this.status!==PENDING)return;
            this.status=FULFILLED;
            this.value=value;
            while(this.successCallback.length)this.successCallback.shift()()
        };

        reject=reason=>{
            if(this.status!==PENDING)return;
            this.status=REJECTED;
            this.reason=reason;
            while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
        };

        then(successCallback,failCallback){
           successCallback=successCallback?successCallback:value=>value;
           failCallback=failCallback?failCallback:reason=>{throw reason};

            let prmoise2=new MyPromise((resolve,reject)=>{
                if(this.status===FULFILLED){ 
                    setTimeout(() => {
                        try{
                             let n=successCallback(this.value)
                             resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }else if(this.status===REJECTED){ 
                    setTimeout(() => {
                        try{
                             let n= failCallback(this.reason)
                             resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                   
                }else{ 
                    this.successCallback.push(()=>{
                        setTimeout(() => {
                            try{
                                let n=successCallback(this.value)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    });
                    this.failCallback.push(()=>{
                        setTimeout(() => {
                            try{
                                let n= failCallback(this.reason)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    }); 
                }
            });
            return prmoise2;
        };
        /*
          all()实现all方法
        */
        static all(array){
            // 要返回的数组
            let result=[];
            //记录执行次数
            let index=0;
            //all方法返回的也是一个promise
            return new MyPromise((resolve,reject)=>{

                //定义一个存到result数组中的方法
                function addItem(key,value){
                    result[key]=value;
                    index++;
                    //如果index等于传入array的长度,说明执行完成
                    if(index==array.length){
                        resolve(result)
                    }
                }

                //循环array
                for(let i=0;i<array.length;i++){
                    //当前的参数
                    let current=array[i];
                    //判断一下当前的返回值是普通值还是promise
                    if(current instanceof MyPromise){
                        current.then(value=>addItem(i,value),reason=>reject(reason));
                    }else{
                        addItem(i,current)
                    }
                
                }
            })
        }
    }

    function resolvePromise(prmoise2,n,resolve,reject){
        if(prmoise2===n){
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        if(n instanceof MyPromise){
            n.then(resolve,reject)
        }else{
            resolve(n)
        }
    }

    module.exports = MyPromise

Promise.resolve()方法实现

resolve方法可以接受一个prmoise对象,也可以接收一个普通值,如果是普通值需要包装成一个prmoise返回。

代码实现

    const PENDING='pending', 
          FULFILLED='fulfilled', 
          REJECTED='rejected';

    class MyPromise{
        constructor(exector){
          try{
            exector(this.resolve,this.reject)
          }catch(e){
              reject(e)
          }
        };

        status=PENDING;

        value=undefined;
        reason=undefined;

        successCallback=[];
        failCallback=[];

        resolve=value=>{
            if(this.status!==PENDING)return;
            this.status=FULFILLED;
            this.value=value;
            while(this.successCallback.length)this.successCallback.shift()()
        };

        reject=reason=>{
            if(this.status!==PENDING)return;
            this.status=REJECTED;
            this.reason=reason;
            while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
        };

        then(successCallback,failCallback){
           successCallback=successCallback?successCallback:value=>value;
           failCallback=failCallback?failCallback:reason=>{throw reason};

            let prmoise2=new MyPromise((resolve,reject)=>{
                if(this.status===FULFILLED){ 
                    setTimeout(() => {
                        try{
                             let n=successCallback(this.value)
                             resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }else if(this.status===REJECTED){ 
                    setTimeout(() => {
                        try{
                             let n= failCallback(this.reason)
                             resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                   
                }else{ 
                    this.successCallback.push(()=>{
                        setTimeout(() => {
                            try{
                                let n=successCallback(this.value)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    });
                    this.failCallback.push(()=>{
                        setTimeout(() => {
                            try{
                                let n= failCallback(this.reason)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    }); 
                }
            });
            return prmoise2;
        };
       
        static all(array){
          
            let result=[];
            let index=0;
            return new MyPromise((resolve,reject)=>{
                function addItem(key,value){
                    result[key]=value;
                    index++;
                    if(index==array.length){
                        resolve(result)
                    }
                }

                for(let i=0;i<array.length;i++){
                    let current=array[i];
                    if(current instanceof MyPromise){
                        current.then(value=>addItem(i,value),reason=>reject(reason));
                    }else{
                        addItem(i,current)
                    }
                
                }
            })
        };
        /* 
         resolve方法
        */
         static resolve(value){
            //判断value 是否为MyPromise的实例,如果是,直接返回
            if( value instanceof MyPromise) return value;
            //如果不是,return一个prmoise
            return new MyPromise(resolve=>resolve(value))
        }
    }

    function resolvePromise(prmoise2,n,resolve,reject){
        if(prmoise2===n){
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        if(n instanceof MyPromise){
            n.then(resolve,reject)
        }else{
            resolve(n)
        }
    }

    module.exports = MyPromise

代码验证

    const MyPromise = require('./myPromise');

    MyPromise.resolve('lonjin').then((res)=>{
        console.log(res)
    });
    //lonjin

finally()方法实现

finally()方法返回一个Promise。在Promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。
这避免了同样的语句需要在then()catch()中各写一次的情况。

  • 返回的是Promise
  • 无论成功和失败都会执行

代码实现

    //MyPromise中
   finally(callback){
        // 使用then方法拿到当前的promise的状态
        return this.then(value=>{
            /* 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
               把callback调用之后返回的promise传递过去,并且执行promise,且在成功之后返回value
            */
            return MyPromise.resolve(callback()).then(()=>value)
        },reason=>{
             // 失败之后调用的then方法,然后把失败的原因返回出去
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
    }

代码验证

    const MyPromise = require('./myPromise')

    function fn1(){
    return new MyPromise((resolve,reject)=>{
        setTimeout(() => {
        reject('100')
        }, 3000);
    })
    }

    fn1().finally(()=>{
    console.log('finally')
    }).then((value)=>{
    console.log(value)
    },(erro)=>{
    console.log(erro)
    })
    //finally
    // 100

catch方法的实现

  • catch方法是为了捕获promise对象的所有错误回调的
  • 直接调用then方法,然后成功的地方传递undefined,错误的地方传递reason
  • catch方法是作用在原型对象上的方法

代码实现

    catch (failCallback) {
         // 直接调用then方法,然后成功的地方传递undefined,错误的地方传递reason
        return this.then(undefined, failCallback)
    }

完整代码

完整代码在下方,所有的都加了注释,需要的直接复制到编辑器看吧!


    //定义三个常量
    const PENDING='pending', //等待
        FULFILLED='fulfilled', //成功
        REJECTED='rejected'; //失败

    class MyPromise{
        constructor(exector){
            // 立即执行函数,传入resolve方法和reject方法
            //同时捕获一下错误
            try{
                exector(this.resolve,this.reject)
            }catch(e){
                this.reject(e)
            } 
        };

        //定义一个初始状态
        status=PENDING;

        //保存成功后的值
        value=undefined;

        //保存失败后的值
        reason=undefined;

        //定义一个成功回调函数
        successCallback=[];
        //定义一个失败回调函数
        failCallback=[];

        //成功
        resolve=value=>{
            //判断当前状态是否为PENDING,如果不是就return
            if(this.status!==PENDING)return;
            //更改状态为fulfilled
            this.status=FULFILLED;
            //保存成功的返回值
            this.value=value;
            //判断成功回调是否存在,如果存在,直接调用
            while(this.successCallback.length)this.successCallback.shift()()
        };
        //失败
        reject=reason=>{
            //判断当前状态是否为PENDING,如果不是就return
            if(this.status!==PENDING)return;
            //更改状态为rejected
            this.status=REJECTED;
            //保存失败的返回值
            this.reason=reason;
            //判断成功回调是否存在,如果存在,直接调用
            while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
        };

        //then方法会接收两个回调函数,分别为成功和失败的回调函数
        then(successCallback,failCallback){
            //处理then方法可选参数
            successCallback=successCallback?successCallback:value=>value;
            failCallback=failCallback?failCallback:reason=>{throw reason}
            
            let prmoise2=new MyPromise((resolve,reject)=>{
                //判断当前状态,然后调用相应的函数
                if(this.status===FULFILLED){  //成功状态
                    //传入返回值
                    /*因为new Promise需要执行完成之后才有promise2,同步代码中没有pormise2,
                    所以这部分代码需要异步执行 */
                    setTimeout(() => {
                        //捕获错误
                        try{
                            let n=successCallback(this.value)
                            resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }else if(this.status===REJECTED){ //失败状态  
                    //传入返回值
                    setTimeout(() => {
                        //捕获错误
                        try{
                            let n= failCallback(this.reason)
                            resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }else{ //等待状态
                    //保存回调函数(考虑到异步情况)
                    this.successCallback.push(()=>{
                        setTimeout(() => {
                            //捕获错误
                            try{
                                let n=successCallback(this.value)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    });
                    this.failCallback.push(()=>{
                        setTimeout(() => {
                            //捕获错误
                            try{
                                let n= failCallback(this.reason)
                                resolvePromise(prmoise2,n,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    }); 
                }
            });
            return prmoise2;
            
        };
        // 直接调用then方法,然后成功的地方传递undefined,错误的地方传递reason
        catch (failCallback) {
            return this.then(undefined, failCallback)
        }

        finally(callback){
            // 使用then方法拿到当前的promise的状态
            return this.then(value=>{
                /* 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
                把callback调用之后返回的promise传递过去,并且执行promise,且在成功之后返回value
                */
                return MyPromise.resolve(callback()).then(()=>value)
            },reason=>{
                // 失败之后调用的then方法,然后把失败的原因返回出去
                return MyPromise.resolve(callback()).then(() => { throw reason })
            })
        }

        static all(array){
            // 要返回的数组
            let result=[];
            //记录执行次数
            let index=0;
            //all方法返回的也是一个promise
            return new MyPromise((resolve,reject)=>{

                //定义一个存到result数组中的方法
                function addItem(key,value){
                    result[key]=value;
                    index++;
                    if(index==array.length){
                        resolve(result)
                    }
                }

                //循环array
                for(let i=0;i<array.length;i++){
                    //当前的参数
                    let current=array[i];

                    //判断一下当前的返回值是普通值还是promise
                    if(current instanceof MyPromise){
                        current.then(value=>addItem(i,value),reason=>reject(reason));
                    }else{
                        addItem(i,current)
                    }
                
                }
            })
        };

        static resolve(value){
            //判断value 是否为MyPromise的实例,如果是,直接返回
        if( value instanceof MyPromise) return value;
        //如果不是,return一个prmoise
        return new MyPromise(resolve=>resolve(value))
        }
    }

    //判断x是不是其实例的对象
    function resolvePromise(prmoise2,n,resolve,reject){
        //如果n和prmoise2相等 则返回错误提示
        if(prmoise2===n){
            console.log('相等')
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        // 判断x是不是其实例对象
        if(n instanceof MyPromise){
            n.then(resolve,reject)
        }else{
            //普通值,直接调用resolve
            resolve(n)
        }
    }

    //导出MyPromise
    module.exports = MyPromise
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容