手寫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是一個類

  • 它有三種狀態(tài):等待(pending)棱貌、成功(fulfilled)失敗(rejected)箕肃,狀態(tài)一旦從pending變成fulfilledrejected就不可更改

  • new Promise中傳入了一個函數婚脱,我們可以把它稱之為執(zhí)行器,執(zhí)行器會立即執(zhí)行

  • 在執(zhí)行器中傳入了resolvereject,用來改變它的狀態(tài)

  • then方法內部做的事情就是判斷狀態(tài)勺像,如果狀態(tài)是成功障贸,調用成功回調函數,如果狀態(tài)是失敗,就調用失敗回調函數,且then方法會接收一個成功或者失敗的值吟宦。

代碼實現

  • 我們先建立一個MyPromise.js文件篮洁,在這個文件中編寫我們的Promise。
    //定義三個常量
    const PENDING='pending', //等待
        FULFILLED='fulfilled', //成功
        REJECTED='rejected'; //失敗

    class MyPromise{
        constructor(exector){
            // 立即執(zhí)行函數殃姓,傳入resolve方法和reject方法
            exector(this.resolve,this.reject)
        };

        //定義一個初始狀態(tài)
        status=PENDING;

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

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

        //成功
        resolve=value=>{
            //判斷當前狀態(tài)是否為PENDING嘀粱,如果不是就return
            if(this.status!==PENDING)return;
            //更改狀態(tài)為fulfilled
            this.status=FULFILLED;
            //保存成功的返回值
            this.value=value;

        };
        //失敗
        reject=reason=>{
            //判斷當前狀態(tài)是否為PENDING,如果不是就return
            if(this.status!==PENDING)return;
            //更改狀態(tài)為rejected
            this.status=REJECTED;
            //保存失敗的返回值
            this.reason=reason;
        };

        //then方法會接收兩個回調函數辰狡,分別為成功和失敗的回調函數
        then(successCallback,failCallback){
            //判斷當前狀態(tài)锋叨,然后調用相應的函數
            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會馬上執(zhí)行偷卧,但此時狀態(tài)還為pending之前并沒有判斷等待這個狀態(tài)
        setTimeout(() => {
            resolve(200)
        }, 100);
    })

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

處理異步調用

  • 由于執(zhí)行器可能有異步代碼,所以我們在then方法中還需要判斷一下狀態(tài)吆倦,然后做相應的處理

代碼如下(為了思路清晰听诸,只對做更改的地方加注釋,會標注實現步驟蚕泽,按步驟閱讀即可):

    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
                -------------------------------------------------
                等待狀態(tài)泛领,處理異步邏輯
                由于不知道狀態(tài),所以我們需要把成功和失敗的回調函數先保存起來
                保存回調函數 */
                this.successCallback=successCallback;
                this.failCallback=failCallback;
                
            }
        }
    }
    //導出MyPromise
    module.exports = MyPromise
  • 驗證代碼:
    //index.js
    const MyPromise=require('./MyPromise');

    let n=new MyPromise((resolve,reject)=>{
        //由于setTimeout為異步代碼敛惊,then會馬上執(zhí)行渊鞋,但此時狀態(tài)還為pending之前并沒有判斷等待這個狀態(tài)
        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回調函數中取數組最后一個保存的回調執(zhí)行即可

    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數組中是否有值讼油,有值的話就取數組最后的執(zhí)行
            */
            while(this.successCallback.length)this.successCallback.shift()(this.value)
        };
    
        reject=reason=>{
        
            if(this.status!==PENDING)return;
            
            this.status=REJECTED;
        
            this.reason=reason;
            /* 步驟4
                -------------------------------------
                判斷failCallback數組中是否有值,有值的話就取數組最后的執(zhí)行
            */
            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方法的鏈式調用

還是先理一下需求,再進行開發(fā):

  • 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,所以這里需要創(chuàng)建一個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對象确虱,則會發(fā)生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中傳入的立即執(zhí)行器中代碼報錯校辩,以及then()中寫的代碼報錯,我們都需要捕獲到辆童,然后執(zhí)行reject

:three: 之前一直是處理resolve的情況宜咒,還需要處理一下reject的情況

這時候我們需要改造一下剛才寫的代碼:

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

    class MyPromise{
        /* 步驟1
        -------------------
        捕獲一下立即執(zhí)行器中的錯誤
        */
        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需要執(zhí)行完成之后才有promise2故黑,同步代碼中沒有pormise2,
                    所以這部分代碼需要異步執(zhí)行 ,我們把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('執(zhí)行器拋出異常!')
        
    })
    errofn.then((val)=>{
        console.log(val)
    },(erro)=>{
        console.log(erro)
    })
    // 執(zhí)行器錯誤

將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=[];
            //記錄執(zhí)行次數
            let index=0;
            //all方法返回的也是一個promise
            return new MyPromise((resolve,reject)=>{

                //定義一個存到result數組中的方法
                function addItem(key,value){
                    result[key]=value;
                    index++;
                    //如果index等于傳入array的長度夯尽,說明執(zhí)行完成
                    if(index==array.length){
                        resolve(result)
                    }
                }

                //循環(huán)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蛾娶,都會執(zhí)行指定的回調函數灯谣。這為在Promise是否成功完成后都需要執(zhí)行的代碼提供了一種方式。
這避免了同樣的語句需要在then()catch()中各寫一次的情況蛔琅。

  • 返回的是Promise
  • 無論成功和失敗都會執(zhí)行

代碼實現

    //MyPromise中
   finally(callback){
        // 使用then方法拿到當前的promise的狀態(tài)
        return this.then(value=>{
            /* 如果callback是一個異步的promise對象胎许,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
               把callback調用之后返回的promise傳遞過去揍愁,并且執(zhí)行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){
            // 立即執(zhí)行函數北秽,傳入resolve方法和reject方法
            //同時捕獲一下錯誤
            try{
                exector(this.resolve,this.reject)
            }catch(e){
                this.reject(e)
            } 
        };

        //定義一個初始狀態(tài)
        status=PENDING;

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

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

        //定義一個成功回調函數
        successCallback=[];
        //定義一個失敗回調函數
        failCallback=[];

        //成功
        resolve=value=>{
            //判斷當前狀態(tài)是否為PENDING葡幸,如果不是就return
            if(this.status!==PENDING)return;
            //更改狀態(tài)為fulfilled
            this.status=FULFILLED;
            //保存成功的返回值
            this.value=value;
            //判斷成功回調是否存在,如果存在贺氓,直接調用
            while(this.successCallback.length)this.successCallback.shift()()
        };
        //失敗
        reject=reason=>{
            //判斷當前狀態(tài)是否為PENDING蔚叨,如果不是就return
            if(this.status!==PENDING)return;
            //更改狀態(tài)為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)=>{
                //判斷當前狀態(tài),然后調用相應的函數
                if(this.status===FULFILLED){  //成功狀態(tài)
                    //傳入返回值
                    /*因為new Promise需要執(zhí)行完成之后才有promise2扬蕊,同步代碼中沒有pormise2搀别,
                    所以這部分代碼需要異步執(zhí)行 */
                    setTimeout(() => {
                        //捕獲錯誤
                        try{
                            let n=successCallback(this.value)
                            resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }else if(this.status===REJECTED){ //失敗狀態(tài)  
                    //傳入返回值
                    setTimeout(() => {
                        //捕獲錯誤
                        try{
                            let n= failCallback(this.reason)
                            resolvePromise(prmoise2,n,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }else{ //等待狀態(tài)
                    //保存回調函數(考慮到異步情況)
                    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的狀態(tài)
            return this.then(value=>{
                /* 如果callback是一個異步的promise對象歇父,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
                把callback調用之后返回的promise傳遞過去再愈,并且執(zhí)行promise榜苫,且在成功之后返回value
                */
                return MyPromise.resolve(callback()).then(()=>value)
            },reason=>{
                // 失敗之后調用的then方法,然后把失敗的原因返回出去
                return MyPromise.resolve(callback()).then(() => { throw reason })
            })
        }

        static all(array){
            // 要返回的數組
            let result=[];
            //記錄執(zhí)行次數
            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)
                    }
                }

                //循環(huán)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閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疟暖,死亡現場離奇詭異,居然都是意外死亡田柔,警方通過查閱死者的電腦和手機俐巴,發(fā)現死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硬爆,“玉大人欣舵,你說我怎么就攤上這事∽嚎模” “怎么了缘圈?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵劣光,是天一觀的道長。 經常有香客問我糟把,道長绢涡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任遣疯,我火速辦了婚禮雄可,結果婚禮上,老公的妹妹穿的比我還像新娘缠犀。我一直安慰自己滞项,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布夭坪。 她就那樣靜靜地躺著文判,像睡著了一般。 火紅的嫁衣襯著肌膚如雪室梅。 梳的紋絲不亂的頭發(fā)上戏仓,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音亡鼠,去河邊找鬼赏殃。 笑死,一個胖子當著我的面吹牛间涵,可吹牛的內容都是我干的仁热。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼勾哩,長吁一口氣:“原來是場噩夢啊……” “哼抗蠢!你這毒婦竟也來了?” 一聲冷哼從身側響起思劳,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤迅矛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后潜叛,有當地人在樹林里發(fā)現了一具尸體秽褒,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年威兜,在試婚紗的時候發(fā)現自己被綠了销斟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡椒舵,死狀恐怖蚂踊,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情逮栅,我是刑警寧澤悴势,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布窗宇,位于F島的核電站,受9級特大地震影響特纤,放射性物質發(fā)生泄漏军俊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一捧存、第九天 我趴在偏房一處隱蔽的房頂上張望粪躬。 院中可真熱鬧,春花似錦昔穴、人聲如沸镰官。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泳唠。三九已至,卻和暖如春宙搬,著一層夾襖步出監(jiān)牢的瞬間笨腥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工勇垛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脖母,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓闲孤,卻偏偏與公主長得像谆级,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子讼积,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容