不積跬步之手寫Promise(中)

promise.jpeg

書接上回

8.指定多個回調(diào)

這個是什么意思呢? 在原生Promise中有這樣一個特性.

let p = new Promise((resolve,reject)=>{
    //異步調(diào)用
    setTimeout(()=>{
        resolve("OK");
    },100)
});
p.then(value=>{
    console.log(111)
},reason => {
    console.log(reason)
})

p.then(value=>{
    console.log(222)
},reason => {
    console.log(reason)
})
//輸出
//111
//222

Promise指定多個回調(diào)后,狀態(tài)改變之后,這些回調(diào)全部都會執(zhí)行.我們就是要實(shí)現(xiàn)的這個功能.

在之前保存回調(diào)函數(shù)的時(shí)候,我們使用的是對象來保存的.而如果想要保存多個回調(diào)函數(shù),就需要用到數(shù)組來保存.

修改后的代碼如下:

function PromiseA(executor){
    //添加屬性
    this.PromiseState = "pending";
    this.PromiseResult = null;
    //-------------- 修改的代碼-----------------
    this.callbacks = [];
    //-------------- 修改的代碼-----------------
    //預(yù)先保存實(shí)例對象的this值
    const self = this;
    
    //resolve函數(shù)
    function resolve(data){
        //判斷狀態(tài)
        if(self.PromiseState !== "pending"){
            return ;
        }
        //1.修改對象的狀態(tài)(PromiseState)
        self.PromiseState = "fulfilled";
        //2.設(shè)置對象的結(jié)果值(PromiseResult)
        self.PromiseResult = data;
        //-------------- 修改的代碼-----------------
        //調(diào)用成功的回調(diào)函數(shù)
        self.callback.forEach(item=>{
            item.onResolved(data);
        })
        //-------------- 修改的代碼-----------------
    }
    
    function reject(data){
        //判斷狀態(tài)
        if(self.PromiseState !== "pending"){
            return ;
        }
        //1.修改對象的狀態(tài)(PromiseState)
        self.PromiseState = "rejected";
        //2.設(shè)置對象的結(jié)果值(PromiseResult)
        self.PromiseResult = data;
        //-------------- 修改的代碼-----------------
        //調(diào)用失敗的回調(diào)函數(shù)
        self.callback.forEach(item=>{
            item.onRejected(data);
        })
        //-------------- 修改的代碼-----------------
    }
    
    //處理 throw 拋出的錯誤
    try{
        //同步調(diào)用一下
        executor(resolve,reject);
    }catch (e){
        //通過調(diào)用reject函數(shù),它的內(nèi)部可以修改狀態(tài)的和賦值
        //所以我們這里可以把錯誤直接傳進(jìn)去就可以了.
        reject(e);
    }
}

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
    //調(diào)用 成功 回調(diào)函數(shù)
    if(this.PromiseState === "fulfilled"){
        onResolved(this.PromiseResult);
    }
    //調(diào)用失敗的回調(diào)函數(shù)
    if(this.PromiseState === "rejected"){
        onRejected(this.PromiseResult);
    }
    
    //添加pending狀態(tài)的處理
    if(this.PromiseState === "pending"){
        //-------------- 修改的代碼-----------------
        //保存回調(diào)函數(shù)
        this.callbacks.push({
            onResolved,
            onRejected
        });
        //-------------- 修改的代碼-----------------
    }
}

然后在callbacks調(diào)用的地方改為數(shù)組遍歷的方式進(jìn)行調(diào)用.

9.同步任務(wù) then 返回結(jié)果的實(shí)現(xiàn)

let p = new Promise((resolve,reject)=>{
    resolve("OK");
});
const result = p.then(value=>{
    console.log(111)
},reason => {
    console.log(reason)
})
console.log(result)

看官方的實(shí)現(xiàn), result的結(jié)果由p.then的回調(diào)函數(shù)的執(zhí)行結(jié)果決定.
如果構(gòu)造函數(shù)中成功,就執(zhí)行第一個函數(shù),那就由第一個函數(shù)決定.否則就由第二個決定.

它會返回一個Promise對象,而Promise對象的結(jié)果又取決于 回調(diào)函數(shù)的返回值.
其中又分為三種情況:

  1. 返回非Promise對象的,比如123,ok,不返回就是undefined,那么它默認(rèn)會執(zhí)行返回成功的Promise對象,
  2. 返回Promise對象的,則由這個對象決定狀態(tài),如果這個這個對象是成功的,那么外部result就是成功,如果失敗的,那么外部就是失敗的.
  3. 如果拋出錯誤,那么外部對象就是失敗的.

我們一步一步的來解決上訴的問題:

9.1 既然返回結(jié)果是一個Promise,安排

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
    return new PromiseA((resolve,reject)=>{
        //調(diào)用 成功 回調(diào)函數(shù)
        if(this.PromiseState === "fulfilled"){
            onResolved(this.PromiseResult);
        }
        //調(diào)用失敗的回調(diào)函數(shù)
        if(this.PromiseState === "rejected"){
            onRejected(this.PromiseResult);
        }
        //添加pending狀態(tài)的處理
        if(this.PromiseState === "pending"){
            //保存回調(diào)函數(shù)
            this.callbacks.push({
                onResolved,
                onRejected
            });
        }
    })
}

我們通過new PromiseA來包裹一下then方法中的處理函數(shù). 這一步和官方的Promise的使用很像,

let p = new Promise((resolve,reject)=>{
    //異步調(diào)用
    setTimeout(()=>{
        resolve("OK");
    },100)
});

它的里面通過調(diào)用resolvereject來實(shí)現(xiàn)狀態(tài)的改變.

所以我們這里也可以通過調(diào)用new PromiseA時(shí)自帶的resolvereject函數(shù)來改變返回的PromiseA的狀態(tài).

而返回的PromiseA的狀態(tài)又是取決于回調(diào)函數(shù)的結(jié)果.結(jié)果在哪里?

當(dāng)然是在onResolved(this.PromiseResult)里.

所以

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
    //這個就是返回外部的結(jié)果Promise ,它的內(nèi)部狀態(tài)由 then方法的回調(diào)決定
    return new PromiseA((resolve,reject)=>{
        //調(diào)用 成功 回調(diào)函數(shù)
        if(this.PromiseState === "fulfilled"){
           //-------------- 修改的代碼-----------------
            let result = onResolved(this.PromiseResult);
            //判斷 result 是 Promise 對象
            if(result instanceof PromiseA){
               
            }else{
                //非Promise 數(shù)據(jù) 則成功,通過調(diào)用resolve來修改
                resolve(result);
            }
            //-------------- 修改的代碼-----------------
        }
        //調(diào)用失敗的回調(diào)函數(shù)
        if(this.PromiseState === "rejected"){
           //...
        }
        //添加pending狀態(tài)的處理
        if(this.PromiseState === "pending"){
           //...
        }
    })
}

我們拿到 result的結(jié)果.

 let result = onResolved(this.PromiseResult);

它就是then方法執(zhí)行第一個回調(diào)函數(shù)的返回結(jié)果,來判斷它是否為PromiseA的對象.
如果不是,按照我們上面整理出的規(guī)則,就是成功的狀態(tài),所以通過調(diào)用外部對象的resolve來進(jìn)行改變.

如果判斷對象是Promise呢 ?

返回Promise對象的,則由這個對象決定狀態(tài),如果這個這個對象是成功的,那么外部result就是成功,如果失敗的,那么外部就是失敗的.

那么怎么拿到這個返回Promise對象的狀態(tài)呢?

我們知道當(dāng)Promise對象一旦狀態(tài)發(fā)生改變,它就會回調(diào)它的then方法中的回調(diào).所以我們這里自己去調(diào)用它的then方法.

PromiseA.prototype.then = function (onResolved,onRejected){
    //這個就是返回外部的結(jié)果Promise ,它的內(nèi)部狀態(tài)由 then方法的回調(diào)決定
    return new PromiseA((resolve,reject)=>{
        //調(diào)用 成功 回調(diào)函數(shù)
        if(this.PromiseState === "fulfilled"){
            let result = onResolved(this.PromiseResult);
            //判斷 result 是 Promise 對象
            if(result instanceof PromiseA){
                //-------------- 修改的代碼-----------------
                //如果是Promise對象,我們需要知道這個對象的狀態(tài)是成功還是失敗的?
                //Promise對象如果成功,它一定會回調(diào) onResolved 失敗 會回調(diào) onRejected
                result.then(v=>{
                    //在它的內(nèi)部調(diào)用 resolve 來修改外部 我們創(chuàng)建的那個Promise對象的狀態(tài)
                    resolve(v);
                },r=>{
                    reject(r);
                })
                //-------------- 修改的代碼-----------------
            }else{
                //非Promise 數(shù)據(jù) 則成功,通過調(diào)用resolve來修改
                resolve(result);
            }
        }
        //調(diào)用失敗的回調(diào)函數(shù)
        if(this.PromiseState === "rejected"){
           //...
        }
    
        //添加pending狀態(tài)的處理
        if(this.PromiseState === "pending"){
           //...
        }
    })
}

我們通過在它的內(nèi)部調(diào)用 resolve 來修改外部 我們創(chuàng)建的那個Promise對象的狀態(tài).

最后還有一個的狀態(tài)就是錯誤的處理,我們只需要try catch一下就可以了

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
   //這個就是返回外部的結(jié)果Promise ,它的內(nèi)部狀態(tài)由 then方法的回調(diào)決定
   return new PromiseA((resolve,reject)=>{
       //調(diào)用 成功 回調(diào)函數(shù)
       if(this.PromiseState === "fulfilled"){
           //-------------- 修改的代碼-----------------
           try{
           //-------------- 修改的代碼-----------------
               let result = onResolved(this.PromiseResult);
               //判斷 result 是 Promise 對象
               if(result instanceof PromiseA){
                   //如果是Promise對象,我們需要知道這個對象的狀態(tài)是成功還是失敗的?
                   //Promise對象如果成功,它一定會回調(diào) onResolved 失敗 會回調(diào) onRejected
                   result.then(v=>{
                       //在它的內(nèi)部調(diào)用 resolve 來修改外部 我們創(chuàng)建的那個Promise對象的狀態(tài)
                       resolve(v);
                   },r=>{
                       reject(r);
                   })
               }else{
                   //非Promise 數(shù)據(jù) 則成功,通過調(diào)用resolve來修改
                   resolve(result);
               }
           //-------------- 修改的代碼-----------------    
           }catch (e){
               reject(e);
           }
           //-------------- 修改的代碼-----------------
       }
       //調(diào)用失敗的回調(diào)函數(shù)
       if(this.PromiseState === "rejected"){
           onRejected(this.PromiseResult);
       }
       //添加pending狀態(tài)的處理
       if(this.PromiseState === "pending"){
           //保存回調(diào)函數(shù)
           this.callbacks.push({
               onResolved,
               onRejected
           });
       }
   })
}

測試用例:

let p = new PromiseA((resolve,reject)=>{
   resolve("OK");
});
const result = p.then(value=>{
   
   return new PromiseA((resolve,reject)=>{
       resolve("111")   //輸出一
       //reject("error")//輸出二
   })
   //return 111;//輸出三
   //throw "error" //輸出四
},reason => {
   console.log(reason)
})
console.log(result)

//輸出一
PromiseA {
 PromiseState: 'fulfilled',
 PromiseResult: '111',
 callbacks: []
}
//輸出二
PromiseA {
 PromiseState: 'rejected',
 PromiseResult: 'error',
 callbacks: []
}
//輸出三
PromiseA {
 PromiseState: 'fulfilled',
 PromiseResult: 111,
 callbacks: []
}
//輸出四
PromiseA {
 PromiseState: 'rejected',
 PromiseResult: 'error',
 callbacks: []
}

可以看到結(jié)果發(fā)生了改變

10 異步任務(wù) then 返回結(jié)果

這一次我們解決的是

let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve("OK");
    })
});
const result = p.then(value=>{
    console.log(111)
},reason => {
    console.log(reason)
})
console.log(result)

通過異步調(diào)用resolve來執(zhí)行修改內(nèi)部的狀態(tài).還記得之前我們處理異步的時(shí)候是在哪里嗎?

通過判斷狀態(tài)PromiseStatepending,把回調(diào)函數(shù)onResolvedonRejected保存到實(shí)例對象的callbacks上.

然后在未來改變的那一刻 調(diào)用 這些函數(shù),來實(shí)現(xiàn)狀態(tài)的改變.

而我們現(xiàn)在也同樣需要在這里進(jìn)行處理.它的區(qū)別其實(shí)和上面同步的不大,只不過一個是未來調(diào)用的.一個是當(dāng)下調(diào)用的.

我們把pending狀態(tài)下的回調(diào)函數(shù)做一下處理:

  //添加pending狀態(tài)的處理
    if(this.PromiseState === "pending"){
        //保存回調(diào)函數(shù)
        this.callbacks.push({
            onResolved:function (){
                //執(zhí)行成功的回調(diào)函數(shù)
                let result = onResolved(this.PromiseResult);
                if(result instanceof PromiseA){
                    
                }else{
                    resolve(result);
                }
            },
            onRejected:function (){
                   
            }
        });
    }

當(dāng)onResolved這個函數(shù)在未來被調(diào)用的時(shí)候,this.PromiseResult 這個值就無法拿到了,因?yàn)檫@里不再是對象調(diào)用,發(fā)生了隱式綁定的情況.
而是脫離了當(dāng)前執(zhí)行上下文環(huán)境,所以我們這里需要利用詞法作用域的特性,設(shè)置一個self來處理

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
   const self = this;
    //這個就是返回外部的結(jié)果Promise ,它的內(nèi)部狀態(tài)由 then方法的回調(diào)決定
    return new PromiseA((resolve,reject)=>{
        //調(diào)用 成功 回調(diào)函數(shù)
        if(self.PromiseState === "fulfilled"){
            //...
        }
        //調(diào)用失敗的回調(diào)函數(shù)
        if(self.PromiseState === "rejected"){
            //...
        }
    
        //添加pending狀態(tài)的處理
        if(self.PromiseState === "pending"){
            //保存回調(diào)函數(shù)
            self.callbacks.push({
                onResolved:function (){
                    try{
                        let result = onResolved(self.PromiseResult);
                        //判斷 result 是 Promise 對象
                        if(result instanceof PromiseA){
                            //如果是Promise對象,我們需要知道這個對象的狀態(tài)是成功還是失敗的?
                            //Promise對象如果成功,它一定會回調(diào) onResolved 失敗 會回調(diào) onRejected
                            result.then(v=>{
                                //在它的內(nèi)部調(diào)用 resolve 來修改外部 我們創(chuàng)建的那個Promise對象的狀態(tài)
                                resolve(v);
                            },r=>{
                                reject(r);
                            })
                        }else{
                            //非Promise 數(shù)據(jù) 則成功,通過調(diào)用resolve來修改
                            resolve(result);
                        }
                    }catch (e){
                        reject(e);
                    }
                },
                onRejected:function (){
                    try{
                        let result = onRejected(self.PromiseResult);
                        //判斷 result 是 Promise 對象
                        if(result instanceof PromiseA){
                            //如果是Promise對象,我們需要知道這個對象的狀態(tài)是成功還是失敗的?
                            //Promise對象如果成功,它一定會回調(diào) onResolved 失敗 會回調(diào) onRejected
                            result.then(v=>{
                                //在它的內(nèi)部調(diào)用 resolve 來修改外部 我們創(chuàng)建的那個Promise對象的狀態(tài)
                                resolve(v);
                            },r=>{
                                reject(r);
                            })
                        }else{
                            //非Promise 數(shù)據(jù) 則成功,通過調(diào)用resolve來修改
                            resolve(result);
                        }
                    }catch (e){
                        reject(e);
                    }
                }
            });
        }
    })
}

異步任務(wù)的執(zhí)行回調(diào)處理其實(shí)和同步的一樣,區(qū)別僅僅只是未來執(zhí)行的時(shí)機(jī)而已.

所以在未來改變狀態(tài)的時(shí)候,我們的外部Promise對象的狀態(tài)還是取決于它then方法回調(diào)函數(shù)的返回值.

  1. 如果是非Promise對象,則狀態(tài)為成功,
  2. 如果是Promise對象,則狀態(tài)取決于這個對象的狀態(tài),所以我們需要調(diào)用它的then方法,給它添加上回調(diào)函數(shù),在回調(diào)函數(shù)中去修改外部對象的狀態(tài)
  3. 就是處理一下錯誤

11 優(yōu)化一下then 函數(shù)

發(fā)現(xiàn)在同步任務(wù)中 還有一個rejected狀態(tài)沒有處理,而它的邏輯又和fulfilled狀態(tài)的一樣,區(qū)別只是調(diào)用不同的方法而已:

 //調(diào)用失敗的回調(diào)函數(shù)
if(self.PromiseState === "rejected"){
    try{
        let result = onRejected(self.PromiseResult);
        //判斷 result 是 Promise 對象
        if(result instanceof PromiseA){
            //如果是Promise對象,我們需要知道這個對象的狀態(tài)是成功還是失敗的?
            //Promise對象如果成功,它一定會回調(diào) onResolved 失敗 會回調(diào) onRejected
            result.then(v=>{
                //在它的內(nèi)部調(diào)用 resolve 來修改外部 我們創(chuàng)建的那個Promise對象的狀態(tài)
                resolve(v);
            },r=>{
                reject(r);
            })
        }else{
            //非Promise 數(shù)據(jù) 則成功,通過調(diào)用resolve來修改
            resolve(result);
        }
    }catch (e){
        reject(e);
    }
}

是不是發(fā)現(xiàn)上面的這段代碼重復(fù)了太多次,我們優(yōu)化一下,把這個方法統(tǒng)一成一個函數(shù):

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
    const self = this;
    //這個就是返回外部的結(jié)果Promise ,它的內(nèi)部狀態(tài)由 then方法的回調(diào)決定
    return new PromiseA((resolve,reject)=>{
        //抽象成一個函數(shù)
        function callback(type){
            try{
                let result = type(self.PromiseResult);
                //判斷 result 是 Promise 對象
                if(result instanceof PromiseA){
                    //如果是Promise對象,我們需要知道這個對象的狀態(tài)是成功還是失敗的?
                    //Promise對象如果成功,它一定會回調(diào) onResolved 失敗 會回調(diào) onRejected
                    result.then(v=>{
                        //在它的內(nèi)部調(diào)用 resolve 來修改外部 我們創(chuàng)建的那個Promise對象的狀態(tài)
                        resolve(v);
                    },r=>{
                        reject(r);
                    })
                }else{
                    //非Promise 數(shù)據(jù) 則成功,通過調(diào)用resolve來修改
                    resolve(result);
                }
            }catch (e){
                reject(e);
            }
        }
        
        //調(diào)用 成功 回調(diào)函數(shù)
        if(self.PromiseState === "fulfilled"){
            callback(onResolved);
        }
        //調(diào)用失敗的回調(diào)函數(shù)
        if(self.PromiseState === "rejected"){
            callback(onRejected);
        }
        //添加pending狀態(tài)的處理
        if(self.PromiseState === "pending"){
            //保存回調(diào)函數(shù)
            self.callbacks.push({
                onResolved:function (){
                    callback(onResolved);
                },
                onRejected:function (){
                    callback(onRejected);
                }
            });
        }
    })
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末票彪,一起剝皮案震驚了整個濱河市狂秘,隨后出現(xiàn)的幾起案子绸狐,更是在濱河造成了極大的恐慌筛婉,老刑警劉巖托嚣,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蘑辑,居然都是意外死亡继准,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門棘街,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蟆盐,“玉大人,你說我怎么就攤上這事遭殉∈遥” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵险污,是天一觀的道長痹愚。 經(jīng)常有香客問我富岳,道長,這世上最難降的妖魔是什么拯腮? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任窖式,我火速辦了婚禮,結(jié)果婚禮上动壤,老公的妹妹穿的比我還像新娘萝喘。我一直安慰自己,他們只是感情好琼懊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布阁簸。 她就那樣靜靜地躺著,像睡著了一般哼丈。 火紅的嫁衣襯著肌膚如雪启妹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天醉旦,我揣著相機(jī)與錄音饶米,去河邊找鬼。 笑死车胡,一個胖子當(dāng)著我的面吹牛檬输,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吨拍,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼褪猛,長吁一口氣:“原來是場噩夢啊……” “哼网杆!你這毒婦竟也來了羹饰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤碳却,失蹤者是張志新(化名)和其女友劉穎队秩,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昼浦,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡馍资,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了关噪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸟蟹。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖使兔,靈堂內(nèi)的尸體忽然破棺而出建钥,到底是詐尸還是另有隱情,我是刑警寧澤虐沥,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布熊经,位于F島的核電站泽艘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏镐依。R本人自食惡果不足惜匹涮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望槐壳。 院中可真熱鬧然低,春花似錦、人聲如沸宏粤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绍哎。三九已至来农,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崇堰,已是汗流浹背沃于。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留海诲,地道東北人繁莹。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像特幔,于是被迫代替她去往敵國和親咨演。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容