不积跬步之手写Promise(中)

promise.jpeg

书接上回

8.指定多个回调

这个是什么意思呢? 在原生Promise中有这样一个特性.

let p = new Promise((resolve,reject)=>{
    //异步调用
    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指定多个回调后,状态改变之后,这些回调全部都会执行.我们就是要实现的这个功能.

在之前保存回调函数的时候,我们使用的是对象来保存的.而如果想要保存多个回调函数,就需要用到数组来保存.

修改后的代码如下:

function PromiseA(executor){
    //添加属性
    this.PromiseState = "pending";
    this.PromiseResult = null;
    //-------------- 修改的代码-----------------
    this.callbacks = [];
    //-------------- 修改的代码-----------------
    //预先保存实例对象的this值
    const self = this;
    
    //resolve函数
    function resolve(data){
        //判断状态
        if(self.PromiseState !== "pending"){
            return ;
        }
        //1.修改对象的状态(PromiseState)
        self.PromiseState = "fulfilled";
        //2.设置对象的结果值(PromiseResult)
        self.PromiseResult = data;
        //-------------- 修改的代码-----------------
        //调用成功的回调函数
        self.callback.forEach(item=>{
            item.onResolved(data);
        })
        //-------------- 修改的代码-----------------
    }
    
    function reject(data){
        //判断状态
        if(self.PromiseState !== "pending"){
            return ;
        }
        //1.修改对象的状态(PromiseState)
        self.PromiseState = "rejected";
        //2.设置对象的结果值(PromiseResult)
        self.PromiseResult = data;
        //-------------- 修改的代码-----------------
        //调用失败的回调函数
        self.callback.forEach(item=>{
            item.onRejected(data);
        })
        //-------------- 修改的代码-----------------
    }
    
    //处理 throw 抛出的错误
    try{
        //同步调用一下
        executor(resolve,reject);
    }catch (e){
        //通过调用reject函数,它的内部可以修改状态的和赋值
        //所以我们这里可以把错误直接传进去就可以了.
        reject(e);
    }
}

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
    //调用 成功 回调函数
    if(this.PromiseState === "fulfilled"){
        onResolved(this.PromiseResult);
    }
    //调用失败的回调函数
    if(this.PromiseState === "rejected"){
        onRejected(this.PromiseResult);
    }
    
    //添加pending状态的处理
    if(this.PromiseState === "pending"){
        //-------------- 修改的代码-----------------
        //保存回调函数
        this.callbacks.push({
            onResolved,
            onRejected
        });
        //-------------- 修改的代码-----------------
    }
}

然后在callbacks调用的地方改为数组遍历的方式进行调用.

9.同步任务 then 返回结果的实现

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

看官方的实现, result的结果由p.then的回调函数的执行结果决定.
如果构造函数中成功,就执行第一个函数,那就由第一个函数决定.否则就由第二个决定.

它会返回一个Promise对象,而Promise对象的结果又取决于 回调函数的返回值.
其中又分为三种情况:

  1. 返回非Promise对象的,比如123,ok,不返回就是undefined,那么它默认会执行返回成功的Promise对象,
  2. 返回Promise对象的,则由这个对象决定状态,如果这个这个对象是成功的,那么外部result就是成功,如果失败的,那么外部就是失败的.
  3. 如果抛出错误,那么外部对象就是失败的.

我们一步一步的来解决上诉的问题:

9.1 既然返回结果是一个Promise,安排

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
    return new PromiseA((resolve,reject)=>{
        //调用 成功 回调函数
        if(this.PromiseState === "fulfilled"){
            onResolved(this.PromiseResult);
        }
        //调用失败的回调函数
        if(this.PromiseState === "rejected"){
            onRejected(this.PromiseResult);
        }
        //添加pending状态的处理
        if(this.PromiseState === "pending"){
            //保存回调函数
            this.callbacks.push({
                onResolved,
                onRejected
            });
        }
    })
}

我们通过new PromiseA来包裹一下then方法中的处理函数. 这一步和官方的Promise的使用很像,

let p = new Promise((resolve,reject)=>{
    //异步调用
    setTimeout(()=>{
        resolve("OK");
    },100)
});

它的里面通过调用resolvereject来实现状态的改变.

所以我们这里也可以通过调用new PromiseA时自带的resolvereject函数来改变返回的PromiseA的状态.

而返回的PromiseA的状态又是取决于回调函数的结果.结果在哪里?

当然是在onResolved(this.PromiseResult)里.

所以

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
    //这个就是返回外部的结果Promise ,它的内部状态由 then方法的回调决定
    return new PromiseA((resolve,reject)=>{
        //调用 成功 回调函数
        if(this.PromiseState === "fulfilled"){
           //-------------- 修改的代码-----------------
            let result = onResolved(this.PromiseResult);
            //判断 result 是 Promise 对象
            if(result instanceof PromiseA){
               
            }else{
                //非Promise 数据 则成功,通过调用resolve来修改
                resolve(result);
            }
            //-------------- 修改的代码-----------------
        }
        //调用失败的回调函数
        if(this.PromiseState === "rejected"){
           //...
        }
        //添加pending状态的处理
        if(this.PromiseState === "pending"){
           //...
        }
    })
}

我们拿到 result的结果.

 let result = onResolved(this.PromiseResult);

它就是then方法执行第一个回调函数的返回结果,来判断它是否为PromiseA的对象.
如果不是,按照我们上面整理出的规则,就是成功的状态,所以通过调用外部对象的resolve来进行改变.

如果判断对象是Promise呢 ?

返回Promise对象的,则由这个对象决定状态,如果这个这个对象是成功的,那么外部result就是成功,如果失败的,那么外部就是失败的.

那么怎么拿到这个返回Promise对象的状态呢?

我们知道当Promise对象一旦状态发生改变,它就会回调它的then方法中的回调.所以我们这里自己去调用它的then方法.

PromiseA.prototype.then = function (onResolved,onRejected){
    //这个就是返回外部的结果Promise ,它的内部状态由 then方法的回调决定
    return new PromiseA((resolve,reject)=>{
        //调用 成功 回调函数
        if(this.PromiseState === "fulfilled"){
            let result = onResolved(this.PromiseResult);
            //判断 result 是 Promise 对象
            if(result instanceof PromiseA){
                //-------------- 修改的代码-----------------
                //如果是Promise对象,我们需要知道这个对象的状态是成功还是失败的?
                //Promise对象如果成功,它一定会回调 onResolved 失败 会回调 onRejected
                result.then(v=>{
                    //在它的内部调用 resolve 来修改外部 我们创建的那个Promise对象的状态
                    resolve(v);
                },r=>{
                    reject(r);
                })
                //-------------- 修改的代码-----------------
            }else{
                //非Promise 数据 则成功,通过调用resolve来修改
                resolve(result);
            }
        }
        //调用失败的回调函数
        if(this.PromiseState === "rejected"){
           //...
        }
    
        //添加pending状态的处理
        if(this.PromiseState === "pending"){
           //...
        }
    })
}

我们通过在它的内部调用 resolve 来修改外部 我们创建的那个Promise对象的状态.

最后还有一个的状态就是错误的处理,我们只需要try catch一下就可以了

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
   //这个就是返回外部的结果Promise ,它的内部状态由 then方法的回调决定
   return new PromiseA((resolve,reject)=>{
       //调用 成功 回调函数
       if(this.PromiseState === "fulfilled"){
           //-------------- 修改的代码-----------------
           try{
           //-------------- 修改的代码-----------------
               let result = onResolved(this.PromiseResult);
               //判断 result 是 Promise 对象
               if(result instanceof PromiseA){
                   //如果是Promise对象,我们需要知道这个对象的状态是成功还是失败的?
                   //Promise对象如果成功,它一定会回调 onResolved 失败 会回调 onRejected
                   result.then(v=>{
                       //在它的内部调用 resolve 来修改外部 我们创建的那个Promise对象的状态
                       resolve(v);
                   },r=>{
                       reject(r);
                   })
               }else{
                   //非Promise 数据 则成功,通过调用resolve来修改
                   resolve(result);
               }
           //-------------- 修改的代码-----------------    
           }catch (e){
               reject(e);
           }
           //-------------- 修改的代码-----------------
       }
       //调用失败的回调函数
       if(this.PromiseState === "rejected"){
           onRejected(this.PromiseResult);
       }
       //添加pending状态的处理
       if(this.PromiseState === "pending"){
           //保存回调函数
           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: []
}

可以看到结果发生了改变

10 异步任务 then 返回结果

这一次我们解决的是

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

通过异步调用resolve来执行修改内部的状态.还记得之前我们处理异步的时候是在哪里吗?

通过判断状态PromiseStatepending,把回调函数onResolvedonRejected保存到实例对象的callbacks上.

然后在未来改变的那一刻 调用 这些函数,来实现状态的改变.

而我们现在也同样需要在这里进行处理.它的区别其实和上面同步的不大,只不过一个是未来调用的.一个是当下调用的.

我们把pending状态下的回调函数做一下处理:

  //添加pending状态的处理
    if(this.PromiseState === "pending"){
        //保存回调函数
        this.callbacks.push({
            onResolved:function (){
                //执行成功的回调函数
                let result = onResolved(this.PromiseResult);
                if(result instanceof PromiseA){
                    
                }else{
                    resolve(result);
                }
            },
            onRejected:function (){
                   
            }
        });
    }

onResolved这个函数在未来被调用的时候,this.PromiseResult 这个值就无法拿到了,因为这里不再是对象调用,发生了隐式绑定的情况.
而是脱离了当前执行上下文环境,所以我们这里需要利用词法作用域的特性,设置一个self来处理

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
   const self = this;
    //这个就是返回外部的结果Promise ,它的内部状态由 then方法的回调决定
    return new PromiseA((resolve,reject)=>{
        //调用 成功 回调函数
        if(self.PromiseState === "fulfilled"){
            //...
        }
        //调用失败的回调函数
        if(self.PromiseState === "rejected"){
            //...
        }
    
        //添加pending状态的处理
        if(self.PromiseState === "pending"){
            //保存回调函数
            self.callbacks.push({
                onResolved:function (){
                    try{
                        let result = onResolved(self.PromiseResult);
                        //判断 result 是 Promise 对象
                        if(result instanceof PromiseA){
                            //如果是Promise对象,我们需要知道这个对象的状态是成功还是失败的?
                            //Promise对象如果成功,它一定会回调 onResolved 失败 会回调 onRejected
                            result.then(v=>{
                                //在它的内部调用 resolve 来修改外部 我们创建的那个Promise对象的状态
                                resolve(v);
                            },r=>{
                                reject(r);
                            })
                        }else{
                            //非Promise 数据 则成功,通过调用resolve来修改
                            resolve(result);
                        }
                    }catch (e){
                        reject(e);
                    }
                },
                onRejected:function (){
                    try{
                        let result = onRejected(self.PromiseResult);
                        //判断 result 是 Promise 对象
                        if(result instanceof PromiseA){
                            //如果是Promise对象,我们需要知道这个对象的状态是成功还是失败的?
                            //Promise对象如果成功,它一定会回调 onResolved 失败 会回调 onRejected
                            result.then(v=>{
                                //在它的内部调用 resolve 来修改外部 我们创建的那个Promise对象的状态
                                resolve(v);
                            },r=>{
                                reject(r);
                            })
                        }else{
                            //非Promise 数据 则成功,通过调用resolve来修改
                            resolve(result);
                        }
                    }catch (e){
                        reject(e);
                    }
                }
            });
        }
    })
}

异步任务的执行回调处理其实和同步的一样,区别仅仅只是未来执行的时机而已.

所以在未来改变状态的时候,我们的外部Promise对象的状态还是取决于它then方法回调函数的返回值.

  1. 如果是非Promise对象,则状态为成功,
  2. 如果是Promise对象,则状态取决于这个对象的状态,所以我们需要调用它的then方法,给它添加上回调函数,在回调函数中去修改外部对象的状态
  3. 就是处理一下错误

11 优化一下then 函数

发现在同步任务中 还有一个rejected状态没有处理,而它的逻辑又和fulfilled状态的一样,区别只是调用不同的方法而已:

 //调用失败的回调函数
if(self.PromiseState === "rejected"){
    try{
        let result = onRejected(self.PromiseResult);
        //判断 result 是 Promise 对象
        if(result instanceof PromiseA){
            //如果是Promise对象,我们需要知道这个对象的状态是成功还是失败的?
            //Promise对象如果成功,它一定会回调 onResolved 失败 会回调 onRejected
            result.then(v=>{
                //在它的内部调用 resolve 来修改外部 我们创建的那个Promise对象的状态
                resolve(v);
            },r=>{
                reject(r);
            })
        }else{
            //非Promise 数据 则成功,通过调用resolve来修改
            resolve(result);
        }
    }catch (e){
        reject(e);
    }
}

是不是发现上面的这段代码重复了太多次,我们优化一下,把这个方法统一成一个函数:

//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
    const self = this;
    //这个就是返回外部的结果Promise ,它的内部状态由 then方法的回调决定
    return new PromiseA((resolve,reject)=>{
        //抽象成一个函数
        function callback(type){
            try{
                let result = type(self.PromiseResult);
                //判断 result 是 Promise 对象
                if(result instanceof PromiseA){
                    //如果是Promise对象,我们需要知道这个对象的状态是成功还是失败的?
                    //Promise对象如果成功,它一定会回调 onResolved 失败 会回调 onRejected
                    result.then(v=>{
                        //在它的内部调用 resolve 来修改外部 我们创建的那个Promise对象的状态
                        resolve(v);
                    },r=>{
                        reject(r);
                    })
                }else{
                    //非Promise 数据 则成功,通过调用resolve来修改
                    resolve(result);
                }
            }catch (e){
                reject(e);
            }
        }
        
        //调用 成功 回调函数
        if(self.PromiseState === "fulfilled"){
            callback(onResolved);
        }
        //调用失败的回调函数
        if(self.PromiseState === "rejected"){
            callback(onRejected);
        }
        //添加pending状态的处理
        if(self.PromiseState === "pending"){
            //保存回调函数
            self.callbacks.push({
                onResolved:function (){
                    callback(onResolved);
                },
                onRejected:function (){
                    callback(onRejected);
                }
            });
        }
    })
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容