一、Promise A+ 规范
(一)相关概念
promise:一个具有then 方法的对象/函数,其行为遵循 Promise A+ 规范;
thenable:具有then 方法的对象/函数;
value:promise 实例的状态为兑现/成功时的值,即 resolve 的参数,可为任意类型;
reason:promise 实例的状态为拒绝/失败时的值,即 reject 的值,表示拒绝/失败的原因;
exception:异常值
(二)A+ 规范
- states
Promise 实例的状态,共有三种: pending,fulfilled,rejected。
(1) pending:
- 初始状态,可以改变,在 resolve / reject 执行之前都是这个状态。
- 在 resolve 执行后从 pending 状态改变为 fufilled;
- 在 reject 执行后从 pending 状态改变为 rejected;
(2) fulfilled:
- 是一种最终状态,不可再发生改变;
- 当处于 pending 状态的 promise 在经过resolve之后,其状态会变为 fulfilled;
- 必须有一个 value 值,一般为 resolve 传入的参数,若 resolve 没有传参,则 value 值为 undefined;
(3)rejected:
- 也是一种最终状态,不可再发生改变;
- 当处于 pending 状态的 promis 经过reject后,其状态会变为 rejected;
- 必须有一个 reason 值,一般为 reject 传入的参数,若未传参数,则 reason 值为 undefined;
需要注意的是,promise 的状态只能从 pending 状态转变为 fulfilled 或者 rejected,不可逆转,也不会在 fulfilled 和 rejected 之间转变。因此,一旦 promise 的状态已经是 fulfilled 或者 rejected,即使之后又经过了 resolve 或 reject,promise 的状态也不会再发生变化。
- then方法
根据 A+ 规范,promise 应该提供一个 then 方法,接收两个参数,用于访问最终状态的结果。
1 2 3 4
| const promise = new Promise((resolve, reject) => { }); promise.then(onFulfilled, onRejected);
|
二、如何实现一个 Promise
1.定义状态类型
1 2 3
| const PENDING = "pending", FULFILLED = "fulfilled", REJECTED = "rejected";
|
2.初始化 class
定义初始状态,value以及reason。
1 2 3 4 5 6 7
| class MyPromise { constructor() { this.status = PENDING; this.value = null; this.reason = null; } }
|
3.resolve 和 reject
- 更改 status,从 pending 变为 fulfilled 或 rejected;
更新 value 或 reason 值;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class MyPromise { constructor() { } resolve(value) { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; } } reject(reason) { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; } } }
|
4.构造函数入参
1
| new MyPromise((resolve, reject) => {});
|
- 入参为一个函数 resolver,resolver 接收两个参数:resolve 和 reject;
- 执行 new Promise 时,就会同步执行这个函数,发生任何的错误就会被 reject 出去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class MyPromise { constructor(resolver) { this.status = PENDING; this.value = null; this.reason = null; if (typeof resolver === "function") { try { resolver(this.resolve.bind(this), this.reject.bind(this)); } catch (e) { this.reject(e); } } else { throw new Error(`Promise resolver ${resolver} is not a function`); } } resolve(value) { } reject(reason) { } }
|
5.then 方法
- 入参:onFulfilled,onRejected
- 返回值:新的 promise
- 需要配判断入参是否为函数,如果不是则调用默认函数传递 value 和 reason
先来看看下面这个写法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class MyPromise { constructor(resolver) { } resolve(value) { } reject(reason) { } then(onFulfilled, onRejected) { const realOnFulfilled = isFunction(onFulfilled) ? onFulfilled : (vaule) => value;
const realOnRejected = isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MyPromise((resolve, reject) => { switch (this.status) { case FULFILLED: realOnFulfilled(); break; case REJECTED: realOnRejected(); break; } }); return promise2; } isFunction(func) { return typeof func === "function"; } }
|
这个写法存在问题,只能处理同步操作,一旦有异步执行 resolve 或 reject,则调用 then 方法时,status 仍为 pending。另外,then 方法可以执行多次,因此,需要两个队列来存储realOnFulfilled 和 realOnrejected,一旦 status 状态为 pending,则将 realOnFulfilled 和 realOnRejected 添加进队列里,以便后续 status 值发生变化时依次调用。因此做如下改进:
- 增加两个队列FULFILLED_CALLBACK_LIST和REJECTED_CALLBACK_LIST分别在 pending 状态时存放 realOnFulfilled 和 realOnRejected;
在合适的时机调用队列里的回调函数,有两种方案:
- 在 resolve 和 reject 里,当 status 变为 fulfilled 或 rejected 时调用相应队列里的函数;
- 通过存取器 setter 来监听 status,一旦 status 发生变化,则一次调用相应队列里的处理程序。
这里我选择了后者,能让代码结构更清晰。如果在 resolve 和 reject 里调用,会增加代码的复杂性和混乱程度。
此外,根据 A+ 规范,当 realOnFulfilled 或 realOnRejected 为微任务环境,执行出错时,需要将错误 reject 出去,触发 promise2 的 rejected。且根据其执行得到的结果 a 的不同,会在resolvePromise中有不同的操作。因此使用queueMicrotask( )将其放入微任务队列,并封装到fulfilledMicroTask和rejectedMicroTask中进行错误捕获。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| class MyPromise { FULFILLED_CALLBACK_LIST = []; REJECTED_CALLBACK_LIST = [];
_status = PENDING;
constructor(resolver) { }
set status(newStatus) { this._status = newStatus; switch (newStatus) { case FULFILLED: this.FULFILLED_CALLBACK_LIST.forEach((callback) => callback(this.value) ); break; case REJECTED: this.REJECTED_CALLBACK_LIST.forEach((callback) => callback(this.reason) ); break; } } get status() { return this._status; }
resolve(value) { } reject(reason) { } then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (vaule) => value;
const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MyPromise((resolve, reject) => { const fulfilledMicroTask = () => { queueMicrotask(() => { try { const a = realOnFulfilled(this.value); this.resolvePromise(promise2, a, resolve, reject); } catch (e) { reject(e); } }); }; const rejectedMicroTask = () => { queueMicrotask(() => { try { const a = realOnRejected(this.reason); this.resolvePromise(promise2, a, resolve, reject); } catch (e) { reject(e); } }); };
switch (this.status) { case FULFILLED: fulfilledMicroTask(); break; case REJECTED: rejectedMicroTask(); break; case PENDING: this.FULFILLED_CALLBACK_LIST.push(fulfilledMicroTask); this.REJECTED_CALLBACK_LIST.push(rejectedMicroTask); } }); return promise2; }
resolvePromise(promise2, a, resolve, reject) { }
isFunction(func) { return typeof func === "function"; } }
|
接下来实现resolvePromise来解析 a 的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
resolvePromise(promise2, a, resolve, reject){ if(promise2 === a){ return reject(new TypeError('The promise and the return value are the same')); } if(a instanceof MyPromise){ queueMicroTask(()=>{ a.then( (res) => { this.resolvePromise(promise2, res, resolve, reject); }, reject ) }) }else if(typeof a === 'object' || typeof a === 'function'){ if(a === null){ return resolve(a); } let then = null; try { then = a.then; } catch(e) { return reject(e) } if(this.isFunction(then)){ let hasCalled = false; try { then.call( x, (res) => { if(hasCalled) return; hasCalled = true; this.resolvePromise(promise2, res, resolve, reject) }, (e) => { if(hasCalled) return; hasCalled = true; reject(e) } ) } catch(e) { if(hasCalled){ return } reject(e) } }else{ resolve(a) } }else{ resolve(a) } }
|
6. catch 方法
- catch 实质上就是调用了 then 方法,给第一个参数传入 null,返回一个新的 promise,即 promise2;
- 在 catch 方法未执行完之前,promise2 的 status 将一直是 pending;
- catch 方法执行过程中如果报错,将触发 promise2 的 status 变为 rejected;
- 若 catch 方法执行完毕且没有报错,将触发 promise2 的 status 变为 fulfilled;
- 详见 resolvePromise 方法。
1 2 3 4 5 6 7 8
|
catch(onRejected){ return this.then(null, onRejected) }
|
7. Promise.resolve()
静态方法,用提供的参数创建一个 rosolve 过的 promise。
1 2 3 4 5 6 7 8 9 10 11 12
| class MyPromise {
static resolve(value) { return new MyPromise((resolve) => { resolve(value); }); }
}
|
8. Promise.reject()
与 Promise.resolve 同理。
1 2 3 4 5 6 7 8 9 10 11
| class MyPromise {
static reject(reason) { return new MyPromise((resolve, reject) => { reject(reason); }); }
}
|
至此,Promise 的核心功能都已基本实现。还剩下 Promise.all,Promise.race 等静态方法,有空再研究。以上代码整合如下,如有不足或错误之处,还望不吝指出,感激不尽!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
| const PENDING = "pending", FULFILLED = "fulfilled", REJECTED = "rejected";
class MyPromise { FULFILLED_CALLBACK_LIST = []; REJECTED_CALLBACK_LIST = [];
_status = PENDING;
constructor(resolver) { this.status = PENDING; this.value = null; this.reason = null; if (typeof resolver === "function") { try { resolver(this.resolve.bind(this), this.reject.bind(this)); } catch (e) { this.reject(e); } } else { throw new Error(`Promise resolver ${resolver} is not a function`); } }
set status(newStatus) { this._status = newStatus; switch (newStatus) { case FULFILLED: this.FULFILLED_CALLBACK_LIST.forEach((callback) => callback(this.value) ); break; case REJECTED: this.REJECTED_CALLBACK_LIST.forEach((callback) => callback(this.reason) ); break; } } get status() { return this._status; }
resolve(value) { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; } } reject(reason) { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; } }
then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (vaule) => value;
const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MyPromise((resolve, reject) => { const fulfilledMicroTask = () => { queueMicrotask(() => { try { const a = realOnFulfilled(this.value); this.resolvePromise(promise2, a, resolve, reject); } catch (e) { reject(e); } }); }; const rejectedMicroTask = () => { queueMicrotask(() => { try { const a = realOnRejected(this.reason); this.resolvePromise(promise2, a, resolve, reject); } catch (e) { reject(e); } }); };
switch (this.status) { case FULFILLED: fulfilledMicroTask(); break; case REJECTED: rejectedMicroTask(); break; case PENDING: this.FULFILLED_CALLBACK_LIST.unshift(fulfilledMicroTask); this.REJECTED_CALLBACK_LIST.unshift(rejectedMicroTask); } }); return promise2; }
catch(onRejected) { return this.then(null, onRejected); }
resolvePromise(promise2, a, resolve, reject) { if (promise2 === a) { return reject( new TypeError("The promise and the return value are the same") ); } if (a instanceof MyPromise) { queueMicroTask(() => { a.then((res) => { this.resolvePromise(promise2, res, resolve, reject); }, reject); }); } else if (typeof a === "object" || typeof a === "function") { if (a === null) { return resolve(a); } let then = null; try { then = a.then; } catch (e) { return reject(e); } if (this.isFunction(then)) { let hasCalled = false; try { then.call( x, (res) => { if (hasCalled) return; hasCalled = true; this.resolvePromise(promise2, res, resolve, reject); }, (e) => { if (hasCalled) return; hasCalled = true; reject(e); } ); } catch (e) { if (hasCalled) { return; } reject(e); } } else { resolve(a); } } else { resolve(a); } }
isFunction(func) { return typeof func === "function"; }
static resolve(value) { return new MyPromise((resolve) => { resolve(value); }); }
static reject(reason) { return new MyPromise((resolve, reject) => { reject(reason); }); } }
|