手写一个Promise/A+,完美通过官方872个测试用例 (3)

上面代码then是在实例对象一创建好就调用了,这时候fn里面的异步操作可能还没结束呢,也就是说他的status还是PENDING,这怎么办呢,这时候我们肯定不能立即调onFulfilled或者onRejected的,因为fn到底成功还是失败还不知道呢。那什么时候知道fn成功还是失败呢?答案是fn里面主动调resolve或者reject的时候。所以如果这时候status状态还是PENDING,我们应该将onFulfilled和onRejected两个回调存起来,等到fn有了结论,resolve或者reject的时候再来调用对应的代码。因为后面then还有链式调用,会有多个onFulfilled和onRejected,我这里用两个数组将他们存起来,等resolve或者reject的时候将数组里面的全部方法拿出来执行一遍

// 构造函数 function MyPromise(fn) { // ...省略其他代码... // 构造函数里面添加两个数组存储成功和失败的回调 this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; function resolve(value) { if(that.status === PENDING) { // ...省略其他代码... // resolve里面将所有成功的回调拿出来执行 that.onFulfilledCallbacks.forEach(callback => { callback(that.value); }); } } function reject(reason) { if(that.status === PENDING) { // ...省略其他代码... // resolve里面将所有失败的回调拿出来执行 that.onRejectedCallbacks.forEach(callback => { callback(that.reason); }); } } } // then方法 MyPromise.prototype.then = function(onFulfilled, onRejected) { // ...省略其他代码... // 如果还是PENDING状态,将回调保存下来 if(this.status === PENDING) { this.onFulfilledCallbacks.push(realOnFulfilled); this.onRejectedCallbacks.push(realOnRejected); } }

上面这种暂时将回调保存下来,等条件满足的时候再拿出来运行让我想起了一种模式:订阅发布模式。我们往回调数组里面push回调函数,其实就相当于往事件中心注册事件了,resolve就相当于发布了一个成功事件,所有注册了的事件,即onFulfilledCallbacks里面的所有方法都会拿出来执行,同理reject就相当于发布了一个失败事件。更多订阅发布模式的原理可以看这里。

完成了一小步

到这里为止,其实我们已经可以实现异步调用了,只是then的返回值还没实现,还不能实现链式调用,我们先来玩一下:

var request = require("request"); var MyPromise = require('./MyPromise'); var promise1 = new MyPromise((resolve) => { request('https://www.baidu.com', function (error, response) { if (!error && response.statusCode == 200) { resolve('request1 success'); } }); }); promise1.then(function(value) { console.log(value); }); var promise2 = new MyPromise((resolve, reject) => { request('https://www.baidu.com', function (error, response) { if (!error && response.statusCode == 200) { reject('request2 failed'); } }); }); promise2.then(function(value) { console.log(value); }, function(reason) { console.log(reason); });

上述代码输出如下图,符合我们的预期,说明到目前为止,我们的代码都没问题:

image-20200325172257655

then的返回值

根据规范then的返回值必须是一个promise,规范还定义了不同情况应该怎么处理,我们先来处理几种比较简单的情况:

如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。

MyPromise.prototype.then = function(onFulfilled, onRejected) { // ... 省略其他代码 ... // 有了这个要求,在RESOLVED和REJECTED的时候就不能简单的运行onFulfilled和onRejected了。 // 我们需要将他们用try...catch...包起来,如果有错就reject。 if(this.status === FULFILLED) { var promise2 = new MyPromise(function(resolve, reject) { try { realOnFulfilled(that.value); } catch (error) { reject(error); } }); return promise2; } if(this.status === REJECTED) { var promise2 = new MyPromise(function(resolve, reject) { try { realOnRejected(that.reason); } catch (error) { reject(error); } }); return promise2; } // 如果还是PENDING状态,也不能直接保存回调方法了,需要包一层来捕获错误 if(this.status === PENDING) { var promise2 = new MyPromise(function(resolve, reject) { that.onFulfilledCallbacks.push(function() { try { realOnFulfilled(that.value); } catch (error) { reject(error); } }); that.onRejectedCallbacks.push(function() { try { realOnRejected(that.reason); } catch (error) { reject(error); } }); }); return promise2; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwpxfj.html