2021/07/04 更新日志: 手写一个符合 Promise/A+规范的 Promise 2021/07/05 更新日志: 完成 Promise.resolve(),Promise.reject(),Promise.race();
Promise 在日常开发中经常用到,使用频率相当的高了。为了能够更好的应用 Promise 需要对 Promise 进行一些更深的理解,所以尝试着按照 Promise/A+规范进行了一次手撕代码。
Promise/A+的内容就不细致展开描述一边了,直接开始手撕代码,将规范的内容分解开始进行实践。(具体原版描述请看 Promise/A+)
手撕一个 Promise Promise 有三个状态,两个过程,FULFILLED 有一个 value,REJECTED 有一个 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 const PENDING = "pending" ;const FULFILLED = "fulfilled" ;const REJECTED = "rejected" ;class MyPromise { constructor ( ) { this .status = PENDING; this .value = null ; this .reason = null ; } resolve (value ) { if (this .status === PENDING) { this .status = FULFILLED; this .value = value; } } reject (reason ) { if (this .status === PENDING) { this .status = REJECTED; this .reason = reason; } } }
在我们使用 Promise 的时,可以这样:
const promise = new Promise ((resolve,reject )=> { })
所以我们还需要在构造函数中传入一个函数,并且在初始化的时候传入的函数就已经执行了。
class MyPromise { constructor (fn ) { this .status = PENDING; this .value = null ; this .reason = null ; try { fn(this .resolve.bind(this ),this .reject.bind(this )) }catch (e){ this .reject(e); } } }
按照规范所说,then 方法接收两个参数 onFulfilled,onRejected.并且返回的是一个也是一个 Promise。
class MyPromise { then (onFulfilled,onRejected ) { const promise2 = new MyPromise(); return promise2; } }
onFulfilled/onRejected 必须是个函数,如果不是一个函数则要忽略它,但是我们在使用过程中可以
const promise = new Promise ((resolve,reject )=> { }).then(11111 )
这是因为传入非函数时,参数会被处理成函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class MyPromise { isFunction (param ) { return typeof param === "function" } then (onFulfilled,onRejected ) { const realOnFulfilled = this .isFunction(onFulfilled) ?onFulfilled:(value )=> { return value; } const realOnRejected = this .isFunction(onRejected) ? onRejected:(reason )=> { throw reason; } const promise2 = new MyPromise(); return promise2; } }
然后根据当前状态调用不同的函数
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 class MyPromise { isFunction (param ) { return typeof param === "function" } then (onFulfilled,onRejected ) { const realOnFulfilled = this .isFunction(onFulfilled) ? onFulfilled:(value )=> { return value; } const realOnRejected = this .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; } }
这时可能 status 还没变成终态,因此需要搞个监听,当 status 变成终态时在调用对应的回调。所以,如果这时是 pending 的时候,先将成功和失败的回掉分别存储。
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 class MyPromise { FULFILLED_CALLBACK_LIST = []; REJECTED_CALLBACK_LIST = []; isFunction (param ) { return typeof param === "function" } then (onFulfilled,onRejected ) { const realOnFulfilled = this .isFunction(onFulfilled) ? onFulfilled:(value )=> { return value; } const realOnRejected = this .isFunction(onRejected) ? onRejected:(reason )=> { throw reason; } const promise2 = new MyPromise((resolve,reject )=> { switch (this .status){ case FULFILLED: realOnFulfilled(); break ; case REJECTED: realOnRejected(); break ; case PENDING: this .FULFILLED_CALLBACK_LIST.push(realOnFulfilled); this .REJECTED_CALLBACK_LIST.push(realOnRejected); } }); return promise2; } }
接下来在 status 变化的时候执行所有的回掉。
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 class MyPromise { FULFILLED_CALLBACK_LIST = []; REJECTED_CALLBACK_LIST = []; _status=PENDING; get status (){ return this ._status; } 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 ; } } isFunction (param ) { return typeof param === "function" } then (onFulfilled,onRejected ) { const realOnFulfilled = this .isFunction(onFulfilled) ? onFulfilled:(value )=> { return value; } const realOnRejected = this .isFunction(onRejected) ? onRejected:(reason )=> { throw reason; } const promise2 = new MyPromise((resolve,reject )=> { switch (this .status){ case FULFILLED: realOnFulfilled(); break ; case REJECTED: realOnRejected(); break ; case PENDING: this .FULFILLED_CALLBACK_LIST.push(realOnFulfilled); this .REJECTED_CALLBACK_LIST.push(realOnRejected); } }); return promise2; } }
如果 then 中的 onFulfilled/onRejected 抛出异常了,则 promise2 不能被执行并且返回 e
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 class MyPromise { isFunction (param ) { return typeof param === "function" } then (onFulfilled,onRejected ) { const realOnFulfilled = this .isFunction(onFulfilled) ? onFulfilled:(value )=> { return value; } const realOnRejected = this .isFunction(onRejected) ? onRejected:(reason )=> { throw reason; } const promise2 = new MyPromise((resolve,reject )=> { const fulfilledMicrotask = ()=> { try { realOnFulfilled(this .value); }catch (e){ reject(e) } } const rejectedMicrotask = ()=> { try { realOnRejected(this .reason); }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); break ; } }); return promise2; } }
如果 onFulfilled/onRejected 返回值是 X,那么运行 resolvePromise
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 class MyPromise { isFunction (param ) { return typeof param === "function" } then (onFulfilled,onRejected ) { const realOnFulfilled = this .isFunction(onFulfilled) ? onFulfilled:(value )=> { return value; } const realOnRejected = this .isFunction(onRejected) ? onRejected:(reason )=> { throw reason; } const promise2 = new MyPromise((resolve,reject )=> { const fulfilledMicrotask = ()=> { try { const x = realOnFulfilled(this .value); this .resolvePromise(promise2,x,resolve,reject) }catch (e){ reject(e) } } const rejectedMicrotask = ()=> { try { const x = realOnRejected(this .reason); this .resolvePromise(promise2,x,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); break ; } }); return promise2; } }
接下来完成 resolvePromise 方法。
根据规范,如果 resolvePromise 中 promise2 和 x 是全等,则用 TypeError 作为 reject 的参数。
class MyPromise { resolvePromise (promise2,x,resolve,reject ) { if (promise2 === x){ return reject(new TypeError ("promise2和x不能相同" )) } } }
如果 x 是一个 promise,则采用它的状态。 如果 x 是一个对象或者函数,让 then=x.then,如果此时抛出异常 e,则使用 e 作为 reject 参数。 如果 then 是一个函数,用 x 调用(call)它,第一个参数是 resolvePromise,第二个参数是 rejectPromise。如果同时调用 resolvePromise 和 rejectPromise,或者多次调用同一个参数,保证第一个被调用的有效,后续的调用将被忽略。如果 then 不是一个函数,则用 resolve(x)。 如果 x 不是一个对象或函数,则用 resolve(x)。
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 class MyPromise { resolvePromise (promise2,x,resolve,reject ) { if (promise2 === x){ return reject(new TypeError ("promise2和x不能相同" )) } if (x instanceof MyPromise){ queueMicrotask(()=> { x.then((y )=> { this .resolvePromise(promise2,y,resolve,reject); },reject) }) } else if (this .isObject(x) || this .isFunction(x)){ if (x === null ){ return resolve(x); } let then = null ; try { then = x.then }catch (e){ return reject(e); } if (isFunction(then)){ let called = false ; try { then.call(x,(value )=> { if (called) return ; called = true ; this .resolvePromise(promise2,value,resolve,reject); },(reason )=> { if (called) return ; called = true ; reject(reason) }) }catch (e){ if (called) return ; return reject(e) } } else { resolve(x); } } else { resolve(x); } } }
这个时候其实已经大概写完了。
但是呢,onFulfilled 和 onRejected 都是微任务,所以我们需要给他们再包装一层
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 class MyPromise { then (onFulfilled,onRejected ) { const realOnFulfilled = this .isFunction(onFulfilled) ? onFulfilled:(value )=> { return value; } const realOnRejected = this .isFunction(onRejected) ? onRejected:(reason )=> { throw reason; } const promise2 = new MyPromise((resolve,reject )=> { const fulfilledMicrotask = ()=> { queueMicrotask(()=> { try { const x = realOnFulfilled(this .value); this .resolvePromise(promise2,x,resolve,reject); }catch (e){ reject(e) } }) } const rejectedMicrotask = ()=> { queueMicrotask(()=> { try { const x = realOnRejected(this ,reason); this .resolvePromise(promise2,x,resolve,reject); }catch (e){ reject(e); } }) } }) } }
好了 这就已经写完了。完整代码可见github
使用 promises-aplus-tests 跑了一下测试,全部通过。
在来更新些手写的其他 API
我们在使用 Promise 的时候经常会用到 Promise.resolve()/Promise.reject()这种用法,但是以上写法并不支持直接使用 MyPromise.resolve/reject。这种无需实例即可调用的方法我们称之为静态方法。
Promise.resolve()方法会返回一个 Promise 且状态为 fulfilled。Promise.reject()返回一个 Promise 且状态为 rejected。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class MyPromise { static resolve (value ) { if (value instanceof MyPromise){ return value; } return new MyPromise((resolve )=> { resolve(value); }) } static reject (reason ) { return new MyPromise((resolve,reject )=> { reject(reason); }) } }
race 方法:将多个 Promise 实例,包装成一个新的 Promise 实例。只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class MyPromise { static race (promiseList ) { return MyPromise((resolve,reject )=> { const length = promiseList.length; if (length === 0 ){ return resolve(); } promiseList.forEach((promise )=> { MyPromise.resolve(promise).then((value )=> { return resolve(value) },(reason )=> { return reject(reason) }) }) }) } }