最近在掘金上看了一些关于promise的文章,然后学习了一番,发现是可以自己手写实现一套promise的,于是做了一番学习,现在来整理一下。
Promise状态
promise是有一套规范的,只要实现的promise符合这套规范,就说明显示的promise是可以正常使用的。这套规范的地址是 https://promisesaplus.com/ 。上面除了有规范,还给出了测试工具,方便测试代码。
promise的规范写的很详细,包括用语,各种情况下promise应该有的处理方式。这个就开始实现吧。
规范的一开始就明确定义了(1.1)promise是一个带有then
方法的对象或者函数,所以我们实现的promise需要有一个then
方法;(2.1)promise有三个状态,pending
等待态,fulfilled
成功态,rejected
失败态,而关于这三种状态关系,在(2.1)中有详细的说明:
- 等待态可以转化为成功态或失败态;
- 成功态不能转化为其他状态,而且需要有一个
value
; - 失败态不能转化为其他状态,而且需要有一个
reason
;
(value
跟reason
值不可变,但如果指向是对象的话则指向不可变)
基于以上规范,promise需要有一个状态state
和对应成功态的value
跟失败态的reason
:
1 | const PENDING = 'PENDING'; |
Promise executor
上面代码中constructor
传入了一个executor
,这个是按照原生Promise的写法,声明一个Promise对象实例需要传入executor
,这个executor需要带有两个参数,包括resolve
跟reject
。
resolve
跟reject
是用来判断promise状态的两个函数,要求Promise转化为成功态时要调用resolve
,并传入value,而转化为失败态时要调用reject
,并传入reason。而且,executor
是个立即执行的,是同步的。
当resolve
获得value时,会把值赋给promise的value,并判断当前是否为等待态,是则会把状态改为成功态;而reject
操作类似,把获得的reason赋值给promise的reason,然后判断等待态后把状态改为失败态。
executor
的执行可能出现运行报错,所以需要用try catch
来捕获报错,捕获的报错则传给reject
。
1 | class Promise { |
Promise then
promise/A+规范要求实现的promise需要有then
方法。这个方法需要传入两个回调函数,onFulfilled
和onRejected
。因为支持链式调用,所以也要求函数返回的是一个promise。
首先,对于以上的要求,可以写出下面then
方法的结构:
1 | class Promise { |
then
方法中的onFulfilled
和onRejected
跟promise2
规范中有比较详细的要求,所以我们单独的实现。
onFulfilled
和onRejected
调用要求
首先,只有当promise的状态为成功态时,才可以调用onFulfilled
回调,并传入value
,而只有为失败态时,才可以调用onRejected
回调,并传入reason
,而等待态的情况下,两个回调均不执行,会等待到状态转化之后才会根据状态执行。
同时,由于then
方法支持多次调用,也就是说在promise等待态时,then
方法调用的情况,所以需要在promise内部使用两个数组来储存所有的onFulfilled
和onRejected
方法,并在转化状态后按照先后顺序调用执行。
1 | // Promise constructor |
onFulfilled
和onRejected
结果判断
由于Promise链式调用,在then中执行onFulfilled
和onRejected
返回的值有可能也是一个Promise对象,也有可能是一个其他普通的值,还有可能是抛出一个错误,而这些值都有各自不同的处理要求:
- 如果返回的值是普通值,则调用promise2的resolve;
- 如果返回的值是一个promise,则需要等待执行完把这个promise的
value
或reason
传给promise2的resolve
或reject
; - 如果是抛出一个错误,则调用promise2的reject;
- 如果返回的值等于promise2,则抛出类型错误;
基于上面的要求,可以使用一个resolvePromise
来处理,需要传入以下值:
- 执行
onFulfilled
和onRejected
之后返回的值,x,用来判断处理结果; - promise2的
resolve
或reject
,用来接收x; - promise2,用来判断x是否与之引用相等。
1 | promise2 = new Promise((resolve, reject) => { |
返回值x
的判断
Promise A+规范提供了对x的判断方案。首先,判断x是否为一个方法或者一个对象,因为Promise的实现可以通过Function
或Object
来实现。其次,x带有一个then方法,这是本身Promise规范要求
的方法。除此之外,都判断为普通值。
另外,如果x是一个Promise,那么作为x
成功态返回的值,同样也要判断是否为Promise,这个判断的处理跟x
的处理相同,所以resolvePromise
可以以递归方法的形式实现。
在判断完x
为Promise之后,Promise A+规范有一个比较严谨的考虑,就是为了防止同时出现成功态跟失败态,要在调用promise2的resolve
或reject
之后,禁止再调用promise2的reject
或resolve
。
因此,resolvePromise
实现如下:
1 | function resolvePromise (promise2, x, resolve, reject) { |
测试
以上基本实现基于Promise A+规范的Promise类。最后可以安装promise-aplus-tasts
库对实现的Promise做检验。测试之前需要加以下代码:
1 | Promise.defer = Promise.deferred = function () { |
这段代码本身也实现了一种异步操作代码同步化的处理。最后使用命令promises-aplus-tests xxx.js
执行测试。