之前对于Promise并没有深度的了解过,闲来无事手写了Promise,过程中对Promise有了更进一步的了解,其中比较难的就在于then()
的内部处理,之所以能实现链式调用和异步处理,最重要的核心就是一旦调用了then()
方法,内部就会重启一个新的Promise并将其返回,并根据Promise的状态来做不一样的处理
1 2 3 4 5 6 7 8 ... then(){ const promsie = new MyPromise((resolve, reject) => { ... } return promsie; } ...
1 2 3 4 5 6 7 8 9 10 11 12 13 // 新创建的Promise内部逻辑 // 判断状态 if (this.status === FULFILLED) { fulfilledMicrotask() } else if (this.status === REJECTED) { rejectedMicrotask() } else if (this.status === PENDING) { // 等待 // 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来 // 等到执行成功失败函数的时候再传递 this.onFulfilledCallbacks.push(fulfilledMicrotask); this.onRejectedCallbacks.push(rejectedMicrotask); }
当Promise的内部状态变为fulfilled
或rejected
时会创建一个微任务并调用成功或失败回调,否在就将回调函数缓存起来.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const fulfilledMicrotask = () => { // 创建一个微任务等待 promise2 完成初始化 queueMicrotask(() => { try { // 获取成功回调函数的执行结果 const x = realOnFulfilled(this.value); // 传入 resolvePromise 集中处理 resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }) }
其中还有一个重要的地方就是resolvePromise
方法,此方法会根据回调的返回值的类型做不同的处理,大概得分为两种情况
x不是Promise,直接调用相关方法更改状态;
x是Promise, 创建一个微任务,并将NewPromiseResolveThenableJobTask
方法插入微任务内;而这个方法简单理解就是将返回的Promise链重新包装成一个Promise
当执行NewPromiseResolveThenableJob
时进一步调用PromiseResolveThenableJob==>PerformPromiseThen==>PerformPromiseThenImpl
,PerformPromiseThenImpl
这个方法的内部有一个分支,这里不过多解释,后面示例中会详细说明,但是我们主要记住这个方法内部执行了NewPromiseFulfillReactionJobTask
,创建了一次微任务,所以这个过程中创建了两次微任务,这个过程在前端代码中是无感的,所以在某些情况下,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 39 40 41 42 43 44 //PerformPromiseThenImpl内部有了分支,即当前promise的状态判断执行逻辑 transitioning macro PerformPromiseThenImpl(implicit context: Context)( promise: JSPromise, onFulfilled: Callable|Undefined, onRejected: Callable|Undefined, resultPromiseOrCapability: JSPromise|PromiseCapability|Undefined): void { //pending状态时,将回调缓存,并在promise状态全部完成后推送至任务列队 if (promise.Status() == PromiseState::kPending) { // The {promise} is still in "Pending" state, so we just record a new // PromiseReaction holding both the onFulfilled and onRejected callbacks. // Once the {promise} is resolved we decide on the concrete handler to // push onto the microtask queue. const handlerContext = ExtractHandlerContext(onFulfilled, onRejected); const promiseReactions = UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); const reaction = NewPromiseReaction( handlerContext, promiseReactions, resultPromiseOrCapability, onFulfilled, onRejected); promise.reactions_or_result = reaction; } else { const reactionsOrResult = promise.reactions_or_result; let microtask: PromiseReactionJobTask; let handlerContext: Context; if (promise.Status() == PromiseState::kFulfilled) { handlerContext = ExtractHandlerContext(onFulfilled, onRejected); microtask = NewPromiseFulfillReactionJobTask( handlerContext, reactionsOrResult, onFulfilled, resultPromiseOrCapability); } else deferred { assert(promise.Status() == PromiseState::kRejected); handlerContext = ExtractHandlerContext(onRejected, onFulfilled); //调用NewPromiseFulfillReactionJobTask方法创建任务, microtask = NewPromiseRejectReactionJobTask( handlerContext, reactionsOrResult, onRejected, resultPromiseOrCapability); if (!promise.HasHandler()) { runtime::PromiseRevokeReject(promise); } } //将任务推送任务列队 EnqueueMicrotask(handlerContext, microtask); } promise.SetHasHandler(); }
举例 1.下面这道题应该是比较常见的一道题目了,最终的打印顺序是:0,1,2,3,4,5,6 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 Promise.resolve() .then(() => { //promise_1A console.log(0); return Promise.resolve(4); }) .then((res) => { //promise_Res console.log(res) }) Promise.resolve() .then(() => { //promise_2A console.log(1); }) .then(() => { //promise_2B console.log(2); }) .then(() => { //promise_2C console.log(3); }) .then(() => { //promise_2D console.log(5); }) .then(() =>{ //promise_2E console.log(6); })
逐行分析:
Promise.resolve()
等价于new Promise(resolve=>{resolve()})
,遇到第一个then(),创建一个Promise
(命名promise_1A,状态pending) ,由于前一个Promsie
状态是fulfilled
,直接创建一个微任务,将回调函数放入其中;任务入列
1 2 3 4 5 //加入微任务内 () => { console.log(0); return Promise.resolve(4); }
遇到第二个then,此时前置位Promise(promise1_A)状态为pending
,创建一个Promise
(命名promise_Res,状态pending) ,将回调函数缓存至本地.
第二个Promsie.resolve
同理,再遇到第一个then()时,创建一个Promise
(命名promise_2A,状态pending) ,创建一个微任务并入列;
1 2 3 4 //加入微任务内 () => { console.log(1); }
后面的then依次类推,都分别创建Promise并缓存至本地(promise_2B,promise_2C,promise_2D,promise_2E),此时任务列队里有两个任务[Job(promsie_1A),Job(promise_2A)]
,执行Job(promsie_1A)
,打印0 ;return Promise.resolve(4)
,创建新的promsie,状态为fulfilled;结束回调,返回值为Promise,创建一个微任务并将NewPromiseResolveThenableJobTask
方法放入, 一进一出,此时任务列队为[Job(promise_2A),Job(NewPromiseResolveThenableJobTask)]
继续执行任务Job(promise_2A),打印1 ;回调执行完毕,返回值为undefined,直接调用方法修改promsie(promise_2A)状态为fulfilled;创建微任务并promise_2B的回调加入微任务;此时一进一出,任务列队有两个任务[Job(NewPromiseResolveThenableJobTask),Job(promise_2B)]
执行NewPromiseResolveThenableJobTask
方法,此时内部调用PerformPromiseThenImpl
方法,这里就来到了重要的阶段,此时判断的status就是我们重新包裹的promise即Promise.resolve(4),由于Promise.resolve(4)状态是fulfilled所以创建一个微任务并入列, 一进一出,此时任务列队[Job(promise_2B),microtask(NewPromiseFulfillReactionJobTask)]
执行Job(promise_2B),打印2 ,回调执行完毕,返回undefined,修改状态,创建微任务并将promise_2C的回调加入,此时任务列队[Job(NewPromiseFulfillReactionJobTask),Job(promise_2C)]
;
执行Job(NewPromiseFulfillReactionJobTask),执行完毕后,promise_1A的状态变为fulfilled,创建一个新的微任务,并将promise_Res的回调加入.此时一进一出,任务列队[Job(promise_2C),Job(promise_Res)]
;
执行Job(promise_2C),打印3 ,Job(promsie_2D)入列[Job(promise_Res),Job(promise_2D)]
执行Job(promise_Res),打印4 ,此时第一个promise链结束;Job(promise_2E)入列,[Job(promise_2D),Job(promise_2E)]
依次打印5,6
2. 修改下上面题目的细节,最终的打印顺序是:0,1,新增的then,2,3,4,5,6 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 Promise.resolve() .then(()=>{ //promise_1A console.log(0); return Promise.resolve(4) .then((res) => { promise_新 console.log("新增then"); return res; }); }) .then((res)=>{ //promise_Res console.log(res); }); Promise.resolve() .then(()=> { //promise_2A console.log(1); }) .then(() => { //promise_2B console.log(2); }) .then(() => { //promise_2C console.log(3); }) .then(() => { //promise_2D console.log(5); }) .then(() => { //promise_2E console.log(6); });
和上题的区别是,运行到Promise.resolve(4).then
时,由于Promise.resolve(4)
对应的状态已经是fulfilled,所以会优于NewPromiseResolveThenableJobTask
加入任务列队;当我们运行NewPromiseResolveThenableJobTask
时,内部的Promise.resolve(4).then
状态已经是fulfilled
,任务列队情况如下:
1 2 3 4 5 6 7 8 9 10 [Job(promise_1A),Job(promise_2A)]=>打印0 [Job(promise_2A),Job(promise_新),Job(NewPromiseResolveThenableJob)]=>打印1 [Job(promise_新),Job(NewPromiseResolveThenableJob),Job(promsie_2B)]=>打印新增then [Job(NewPromiseResolveThenableJob),Job(promsie_2B)]=> 此时内部Promise.resolve(4).then状态fulfilled,插入新的微任务 [Job(promsie_2B),Job(NewPromiseFulfillReactionJobTask)]=>打印2 [Job(NewPromiseFulfillReactionJobTask),Job(promsie_2C)]=> [Job(promsie_2C),Job(promsie_Res)]=>打印3 [Job(promsie_Res),Job(promsie_2D)]=>打印4 [Job(promsie_2D),Job(promsie_2E)]=>打印5 [Job(promsie_2E)]=>打印6
3. 再次修改下上面题目的细节,最终的打印顺序是:0,1,第一个新增then,2,第二个新增then,3,5,4,6 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 Promise.resolve() .then(function a() { //promise_1A console.log(0); return Promise.resolve(4) .then((res) => { //promise_新1 console.log("第一个新增then!"); return res; }) .then((res) => { //promise_新2 console.log("第二个新增then"); return res; }) }) .then((res) => { //promise_Res console.log(res); }); Promise.resolve() .then(() => {//promise_2A console.log(1); }) .then(() => { //promise_2B console.log(2); }) .then(() => { //promise_2C console.log(3); }) .then(() => { //promise_2D console.log(5); }) .then(() => { //promise_2E console.log(6); });
和上面的区别是我们在内部又添加了一个then,此时后面的打印结果发生了变化,这个时候就是我们的另一种情况PerformPromiseThenImpl
内部执行的时候,内层包裹的promise链状态此时还是pending
状态,此时不会立即创建一个微任务,而是等待内部promise链的最终状态变为fulfilled
后,才会再次创建一个微任务并入列,任务列队情况如下:
1 2 3 4 5 6 7 8 9 10 11 [Job(promise_1A),Job(promise_2A)]=> 打印0 [Job(promise_2A),Job(promise_新1),NewPromiseResolveThenableJob()]=> 打印1,因为内部第一个新增then状态还是peding,所以第二个新增then缓存本地 [Job(promise_新1),NewPromiseResolveThenableJob(),Job(promsie_2B)]=> 打印第一个新增then [NewPromiseResolveThenableJob(),Job(promsie_2B),Job(promise_新2)]=>此时内部promsie链状态pending,所以将回调缓存等待 [Job(promsie_2B),Job(promise_新2)]=> 打印2 [Job(promise_新2),Job(promsie_2C)]=> 打印第二个新增then [Job(promsie_2C),PerformPromiseThenImpl()]=>打印3,此时内部promise状态完成,创建一个新的微任务并入列 [PerformPromiseThenImpl(),Job(promsie_2D)]=> [Job(promsie_2D),Job(promise_Res)]=>5 [Job(promise_Res),Job(promsie_2E)]=>4 [Job(promsie_2E)]=>6
总结
then内部创建了新的Promise,只用当前一个Promsie状态为fulfilled时,才会将回调包裹在微任务内并排队,否则缓存起来;
当回调返回除了Promise以外的值时,直接调用resolve相关方法并修改状态;
当回调返回的是Promise时,创建一个新的微任务并将NewPromiseResolveThenableJob
方法放入其中;
执行NewPromiseResolveThenableJob
时,底层方法PerformPromiseThenImpl
会根据包裹promise链的状态执行不同的分支,状态fullfilled时,调用NewPromiseFulfillReactionJobTask
创建一个新的微任务,状态为pending时,将回调缓存直到包裹的promise链状态完成后再创建微任务
说明:PerformPromiseThenImpl
中,当状态为pending时,根据源码内的注释,我们可以得出结论,直到这个promise状态变为完成后,会将它推入微任务列队,但是在实际执行中只看到返回了PromiseReaction对象用于记录回调函数,没找到具体在何时推入了微任务列队,不过在一些示例的推演中发现在内部promise全部结束后,只有插入一条微任务后,才能符合实际的打印顺序,所以目前暂时可以按照以上结论来理解,如果后续对此有明确结论.会再次修改本篇文章
1 2 3 4 // The {promise} is still in "Pending" state, so we just record a new // PromiseReaction holding both the onFulfilled and onRejected callbacks. // Once the {promise} is resolved we decide on the concrete handler to // push onto the microtask queue.
参考