先来看下Promise的常见应用:
观察者模式 观察上面的这个例子,我们来分析Promise的调用流程:
Promise的构造方法接受一个executor函数,在new Promise的时候这个executor立即执行
executor内部的异步任务被放入微任务队列等待执行
then被执行,收集成功/失败回调,放入成功/失败队列
executor的异步任务被执行,触发resolve/reject,从成功/失败队列中取出回调依次执行
由上面的分析得知这是一种典型的观察者模式 。这是典型的“收集依赖->触发依赖->取出依赖执行”的方式,被广泛运用于观察者模式 ,在Promise中执行顺序是“then收集依赖->异步触发resolve->resolve执行依赖”。由此我们可以勾勒出Promise的大致形状:
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 class Promise { constructor (executor ){ this .resolveQueue = []; this .rejectQueue = []; let resolve = val => { while (this .resolveQueue .length ) { const callback = this .resolveQueue .shift (); callback (val); } }; let reject = val => { while (this .rejectQueue .length ) { const callback = this .rejectQueue .shift (); callback (val); } }; executor (resolve,reject); } then (resolveFn,rejectFn ){ this .resolveQueue .push (resolveFn); this .rejectQueue .push (rejectFn); } }
Promise A+规范 以上,我们用观察者模式简单实现了能够在then方法的回调中取得异步操作的返回值,下面我们使用Promise A+规范来补充下这个Promise。
Promise 本质是一个状态机,且状态只能为以下三种:Pending(等待态)、Fulfilled(执行态)、Rejected(拒绝态),状态的变更是单向的,只能从 Pending -> Fulfilled 或 Pending -> Rejected,状态变更不可逆 then方法接收两个可选参数,分别对应状态改变时触发的回调。then 方法返回一个 promise。then 方法可以被同一个 promise 调用多次
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 class Promise { constructor (executor ){ this .status = PENDING ; this .resolveQueue = []; this .rejectQueue = []; let resolve = val => { if (this .status !== PENDING ) return ; this .status = FULFILLED ; while (this .resolveQueue .length ) { const callback = this .resolveQueue .shift (); callback (val); } }; let reject = val => { if (this .status !== PENDING ) return ; this .status = REJECTED ; while (this .rejectQueue .length ) { const callback = this .rejectQueue .shift (); callback (val); } }; executor (resolve,reject); } then (resolveFn,rejectFn ){ this .resolveQueue .push (resolveFn); this .rejectQueue .push (rejectFn); } }
then的链式调用 先来看个简单的例子:
思考下如何实现这种链式调用:
promise可以不断地then下去,说明then方法本身会返回一个Promise,那些直接return 一个值的也会被包装为Promise
then的回调需要按顺序执行。以上面的代码为例,虽然中间return了一个Promise,但执行的顺序仍然要保证是1-2-3,我们需要等待Promise的状态变更后再执行下一个then收集的回调
对then方法进行如下改写:
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 then (resolveFn, rejectFn ) { return new Promise ((resolve, reject ) => { const fulfilledFn = value => { try { const x = resolveFn (value); x instanceof Promise ? x.then (resolve, reject) : resolve (x); } catch (err) { reject (err); } }; this .resolveQueue .push (fulfilledFn); const rejectedFn = error => { try { const x = rejectFn (error); x instanceof Promise ? x.then (resolve, reject) : resolve (x); } catch (err) { reject (err); } }; this .rejectQueue .push (rejectedFn); }); }
值穿透 & 状态已变更情况 值穿透:根据规范,如果then接受的函数不是function,那么我们应该忽略它。如果没有忽略,当then回调不为function的时候会抛出异常,导致链式调用中断。 处理状态为resolve/reject的情况:其实我们上边 then() 的写法是对应状态为pendding的情况,但是有些时候,resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()),如果这个时候还把 then()回调 push 进 resolve/reject 的执行队列里,那么回调将不会被执行,因此对于状态已经变为fulfilled或rejected的情况,我们直接执行 then 回调
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 then (resolveFn, rejectFn ) { return new Promise ((resolve, reject ) => { if (typeof resolveFn !== 'function' ) { resolveFn = value => value; } if (typeof rejectFn !== 'function' ) { rejectFn = reason => { throw new Error (reason instanceof Error ? reason.message : reason); }; } const fulfilledFn = value => { try { const x = resolveFn (value); x instanceof Promise ? x.then (resolve, reject) : resolve (x); } catch (err) { reject (err); } }; const rejectedFn = error => { try { const x = rejectFn (error); x instanceof Promise ? x.then (resolve, reject) : resolve (x); } catch (err) { reject (err); } }; switch (this .status ) { case PENDING : this .resolveQueue .push (fulfilledFn); this .rejectQueue .push (rejectedFn); break ; case FULFILLED : fulfilledFn (this .value ); break ; case REJECTED : rejectedFn (this .value ); break ; } }); }
兼容同步任务 完成了 then 的链式调用以后,我们再处理一个前边的细节,然后放出完整代码。上文我们说过,Promise 的执行顺序是 new Promise -> then()回调收集 -> resolve/reject执行回调。这一顺序是建立在executor是异步任务 的前提上的。如果executor是一个同步任务,那么执行顺序会变成new Promise -> resolve/reject的回调 -> then收集回调(即resolve的执行跑到then前面去了,从而导致我们无法在then中得到Promise的resolve的值)。
为了兼容上述情况,我们给resolve/reject执行回调的操作包装一个setTimeout让其异步执行:
这里插一句,有关这个 setTimeout,其实还有一番学问。虽然规范没有要求回调应该被放进宏任务队列还是微任务队列,但其实 Promise 的默认实现是放进了微任务队列,我们的实现(包括大多数 Promise 手动实现和 polyfill 的转化)都是使用 setTimeout 放入了宏任务队列(当然我们也可以用 MutationObserver 模拟微任务)
代码如下:
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 const PENDING = 'pending' ;const FULFILLED = 'fulfilled' ;const REJECTED = 'rejected' ;class Promise { constructor (executor ) { this ._status = PENDING ; this ._value = undefined ; this ._resolveQueue = []; this ._rejectQueue = []; let _resolve = val => { const run = ( ) => { if (this ._status !== PENDING ) return ; this ._status = FULFILLED ; this ._value = val; while (this ._resolveQueue .length ) { const callback = this ._resolveQueue .shift (); callback (val); } }; setTimeout (run); }; let _reject = val => { const run = ( ) => { if (this ._status !== PENDING ) return ; this ._status = REJECTED ; this ._value = val; while (this ._rejectQueue .length ) { const callback = this ._rejectQueue .shift (); callback (val); } }; setTimeout (run); }; executor (_resolve, _reject); } then (resolveFn, rejectFn ) { typeof resolveFn !== 'function' ? resolveFn = value => value : null ; typeof rejectFn !== 'function' ? rejectFn = reason => { thrownewError (reason instanceof Error ? reason.message : reason); } : null ; return new Promise ((resolve, reject ) => { const fulfilledFn = value => { try { let x = resolveFn (value); x instanceof Promise ? x.then (resolve, reject) : resolve (x) } catch (error) { reject (error) } }; const rejectedFn = error => { try { let x = rejectFn (error); x instanceof Promise ? x.then (resolve, reject) : resolve (x) } catch (error) { reject (error) } }; switch (this ._status ) { case PENDING : this ._resolveQueue .push (fulfilledFn); this ._rejectQueue .push (rejectedFn); break ; case FULFILLED : fulfilledFn (this ._value ); break ; case REJECTED : rejectedFn (this ._value ); break ; } }) } }
测试:
Promise中的其他方法 以上我们就实现了Promise的主要功能,剩下的几个方法都非常简单:
Promise.prototype.catch
,返回一个 Promise,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected)
相同:
1 2 3 4 catch (rejectFn) { return this .then (null , rejectFn); }
Promise.prototype.finally()
返回一个 Promise。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。在 finally 之后,还可以继续 then。并且会将值原封不动的传递给后面的then。
1 2 3 4 5 6 finally (callback ) { return this .then ( value => new Promise (resolve => resolve (callback ())).then (() => value), reason => new Promise (resolve => resolve (callback ()).then (() => throw reason)) ); }
Promise.resolve()
返回一个以给定值解析后的 Promise 对象。如果该值为 promise,返回这个 promise;如果这个值是 thenable(即带有”then” 方法)),返回的 promise 会“跟随”这个 thenable 的对象,采用它的最终状态;否则返回的 promise 将以此值完成。此函数将类 promise 对象的多层嵌套展平。
1 2 3 4 static resolve (value ) { return value instanceof Promise ? value : new Promise (resolve => resolve (value)); }
Promise.reject()
方法返回一个带有拒绝原因的 Promise 对象。
1 2 3 static reject (reason ) { return new Promise ((resolve, reject ) => reject (reason)); }
Promise.all(iterable)方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 static all (promiseArr ) { let index = 0 ; let results = []; return new Promise ((resolve, reject ) => { for (let i = 0 ; i < promiseArr.length ; i++) { Promise .resolve (promiseArr[i]).then ( value => { index++; results[i] = value; if (index === promiseArr.length ) { resolve (results); } }, err => { reject (err); } ) } }); }
Promise.race(iterable)方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。
1 2 3 4 5 6 7 8 9 10 11 static race (promiseArr ) { return new Promise ((resolve, reject ) => { for (const p of promiseArr) { Promise .resolve (p).then ( value => resolve (value), err => reject (err) ) } }) }
完整代码 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 const PENDING = 'pending' ;const FULFILLED = 'fulfilled' ;const REJECTED = 'rejected' ;class Promise { constructor (executor ) { this ._status = PENDING ; this ._value = undefined ; this ._resolveQueue = []; this ._rejectQueue = []; let _resolve = val => { const run = ( ) => { if (this ._status !== PENDING ) return ; this ._status = FULFILLED ; this ._value = val; while (this ._resolveQueue .length ) { const callback = this ._resolveQueue .shift (); callback (val); } }; setTimeout (run); }; let _reject = val => { const run = ( ) => { if (this ._status !== PENDING ) return ; this ._status = REJECTED ; this ._value = val; while (this ._rejectQueue .length ) { const callback = this ._rejectQueue .shift (); callback (val); } }; setTimeout (run); }; executor (_resolve, _reject); } then (resolveFn, rejectFn ) { typeof resolveFn !== 'function' ? resolveFn = value => value : null ; typeof rejectFn !== 'function' ? rejectFn = reason => { thrownewError (reason instanceof Error ? reason.message : reason); } : null ; return new Promise ((resolve, reject ) => { const fulfilledFn = value => { try { let x = resolveFn (value); x instanceof Promise ? x.then (resolve, reject) : resolve (x) } catch (error) { reject (error) } }; const rejectedFn = error => { try { let x = rejectFn (error); x instanceof Promise ? x.then (resolve, reject) : resolve (x) } catch (error) { reject (error) } }; switch (this ._status ) { case PENDING : this ._resolveQueue .push (fulfilledFn); this ._rejectQueue .push (rejectedFn); break ; case FULFILLED : fulfilledFn (this ._value ); break ; case REJECTED : rejectedFn (this ._value ); break ; } }) } catch (rejectFn) { return this .then (null , rejectFn); } finally (callback ) { return this .then ( value => new Promise (resolve => resolve (callback ())).then (() => value), reason => new Promise (resolve => resolve (callback ()).then (() => throw reason)) ); } static resolve (value ) { return value instanceof Promise ? value : new Promise (resolve => resolve (value)); } static reject (reason ) { return new Promise ((resolve, reject ) => reject (reason)); } static all (promiseArr ) { let index = 0 ; let results = []; return new Promise ((resolve, reject ) => { for (let i = 0 ; i < promiseArr.length ; i++) { Promise .resolve (promiseArr[i]).then ( value => { index++; results[i] = value; if (index === promiseArr.length ) { resolve (results); } }, err => { reject (err); } ) } }); } static race (promiseArr ) { return new Promise ((resolve, reject ) => { for (const p of promiseArr) { Promise .resolve (p).then ( value => resolve (value), err => reject (err) ) } }) } }