Promise V8 一源码分析2------------引用

 
        3.2 PerformPromiseThenImpl
  1. transitioning macro PerformPromiseThenImpl(implicit context: Context)(

  2. promise: JSPromise, onFulfilled: Callable|Undefined,

  3. onRejected: Callable|Undefined,

  4. resultPromiseOrCapability: JSPromise|PromiseCapability|Undefined): void {

  5. if (promise.Status() == PromiseState::kPending) {

  6. // penging 状态的分支

  7. // The {promise} is still in "Pending" state, so we just record a new

  8. // PromiseReaction holding both the onFulfilled and onRejected callbacks.

  9. // Once the {promise} is resolved we decide on the concrete handler to

  10. // push onto the microtask queue.

  11. const handlerContext = ExtractHandlerContext(onFulfilled, onRejected);

  12. // 拿到 Promise 的 reactions_or_result 字段

  13. const promiseReactions =

  14. UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result);

  15. // 考虑一个 Promise 可能会有多个 then 的情况,reaction 是个链表

  16. // 存 Promise 的所有处理函数

  17. const reaction = NewPromiseReaction(

  18. handlerContext, promiseReactions, resultPromiseOrCapability,

  19. onFulfilled, onRejected);

  20. // reactions_or_result 可以存 Promise 的处理函数,也可以存

  21. // Promise 的最终结果,因为现在 Promise 处于 pending 状态,

  22. // 所以存的是处理函数 reaction

  23. promise.reactions_or_result = reaction;

  24. } else {

  25. // fulfilled 和 rejected 状态的分支

  26. const reactionsOrResult = promise.reactions_or_result;

  27. let microtask: PromiseReactionJobTask;

  28. let handlerContext: Context;

  29. if (promise.Status() == PromiseState::kFulfilled) {

  30. handlerContext = ExtractHandlerContext(onFulfilled, onRejected);

  31. microtask = NewPromiseFulfillReactionJobTask(

  32. handlerContext, reactionsOrResult, onFulfilled,

  33. resultPromiseOrCapability);

  34. } else

  35. deferred {

  36. assert(promise.Status() == PromiseState::kRejected);

  37. handlerContext = ExtractHandlerContext(onRejected, onFulfilled);

  38. microtask = NewPromiseRejectReactionJobTask(

  39. handlerContext, reactionsOrResult, onRejected,

  40. resultPromiseOrCapability);

  41. if (!promise.HasHandler()) {

  42. runtime::PromiseRevokeReject(promise);

  43. }

  44. }

  45. // 即使调用 then 方法时 promise 已经处于 fulfilled 或 rejected 状态,

  46. // then 方法的 onFulfilled 或 onRejected 参数也不会立刻执行,而是进入

  47. // microtask 队列后执行

  48. EnqueueMicrotask(handlerContext, microtask);

  49. }

  50. promise.SetHasHandler();

  51. }

3.2.1 PerformPromiseThenImpl 函数的 penging 分支

PerformPromiseThenImpl 有两个分支,pending 分支调用 NewPromiseReaction 函数,在接收到的 onFulfilled 和 onRejected 参数的基础上,生成 PromiseReaction 对象,存储 Promise 的处理函数,并赋值给 JSPromise 的 reactionsorresult 字段;

考虑一个 Promise 可以会连续调用多个 then 的情况,比如

  1. const myPromise4 = new Promise((resolve, reject) => {

  2. setTimeout(_ => {

  3. resolve('my code delay 5000')

  4. }, 5e3)

  5. })

  6. myPromise4.then(result => {

  7. console.log('第 1 个 then')

  8. })

  9. myPromise4.then(result => {

  10. console.log('第 2 个 then')

  11. })

myPromise4 调用了两次 then 方法,每个 then 方法都会生成一个 PromiseReaction 对象。第一次调用 then 方法时生成对象 PromiseReaction1,此时 myPromise4 的 reactionsorresult 存的是 PromiseReaction1。

第二次调用 then 方法时生成对象 PromiseReaction2,调用 NewPromiseReaction 函数时,PromiseReaction2.next = PromiseReaction1,PromiseReaction1 变成了 PromiseReaction2 的下一个节点,最后 myPromise4 的 reactionsorresult 存的是 PromiseReaction2。PromiseReaction2 后进入 Promise 处理函数的链表,却是链表的头结点。NewPromiseReaction 函数源码如下:

  1. macro NewPromiseReaction(implicit context: Context)(

  2. handlerContext: Context, next: Zero|PromiseReaction,

  3. promiseOrCapability: JSPromise|PromiseCapability|Undefined,

  4. fulfillHandler: Callable|Undefined,

  5. rejectHandler: Callable|Undefined): PromiseReaction {

  6. const nativeContext = LoadNativeContext(handlerContext);

  7. return new PromiseReaction{

  8. map: PromiseReactionMapConstant(),

  9. next: next, // next 字段存的是链表中的下一个节点

  10. reject_handler: rejectHandler,

  11. fulfill_handler: fulfillHandler,

  12. promise_or_capability: promiseOrCapability,

  13. continuation_preserved_embedder_data: nativeContext

  14. [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX]

  15. };

  16. }

在 myPromise4 处于 pending 状态时,myPromise4 的 reactionsorresult 字段示意如下图,下图不是 microtask 队列,下图不是 microtask 队列,下图不是 microtask 队列。

3.2.2 PerformPromiseThenImpl 函数的 fulfilled/rejected 分支

fulfilled/rejected 分支逻辑则简单的多,处理的是当 Promise 处于 fulfilled/rejected 状态时,调用 then 方法的逻辑,以 fulfilled 状态为例,调用 NewPromiseFulfillReactionJobTask 生成 microtask,然后 EnqueueMicrotask(handlerContext, microtask) 将刚才生成的 microtask 放入 microtask 队列。

  1. new Promise((resolve, reject) => {

  2. resolve()

  3. }).then(result => {

  4. console.log('进入 microtask 队列后执行')

  5. })

  6. console.log('同步执行结束')

  7. // 本段代码的打印顺序是:

  8. // 同步执行结束、

  9. // 进入 microtask 队列后执行

尽管调用 then 方法时,Promise 已经处于 resolved 状态,但 then 方法的 onFulfilled 回调函数不会立即执行,而是进入 microtask 队列后执行。

对于一个已经 fulfilled 状态的 Promise,调用其 then 方法时,then 方法接收的 onFulfilled 回调函数不会立即执行。而是进入 microtask 队列后执行,rejected 状态同理 then 方法做的事情简单说就是依赖收集,当 Promise 变成 fulfilled/rejected 状态时,会触发之前收集到的依赖

4.resolve

resolve 函数归根到底调用了 V8 的 FulfillPromise 函数,源码如下:

  1. // https://tc39.es/ecma262/#sec-fulfillpromise

  2. transitioning builtin

  3. FulfillPromise(implicit context: Context)(

  4. promise: JSPromise, value: JSAny): Undefined {

  5. // Assert: The value of promise.[[PromiseState]] is "pending".

  6. // promise 的状态改变是不可逆的

  7. assert(promise.Status() == PromiseState::kPending);

  8. // 取 Promise 的处理函数

  9. const reactions =

  10. UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result);

  11. // Promise 已处于 fulfilled 状态,reactions_or_result 存储的不再是

  12. // 处理函数,而是 Promise 的结果

  13. promise.reactions_or_result = value;

  14. // 设置 Promise 的状态为 fulfilled

  15. promise.SetStatus(PromiseState::kFulfilled);

  16. // Promise 的处理函数,Promise 的结果都拿到了,开始正式处理

  17. TriggerPromiseReactions(reactions, value, kPromiseReactionFulfill);

  18. return Undefined;

  19. }

FulfillPromise 的逻辑是获取 Promise 的处理函数到 reactions,reactions 的类型是 PromiseReaction,是个链表,忘记的同学可以回看上节的那张链表图片;设置 promise 的 reactionsorresult 为 value,这个 value 就是 JavaScript 层传给 resolve 的参数;调用 promise.SetStatus 设置 promise 的状态为 PromiseState::kFulfilled,最后调用 TriggerPromiseReactions。源码如下:

  1. // https://tc39.es/ecma262/#sec-triggerpromisereactions

  2. transitioning macro TriggerPromiseReactions(implicit context: Context)(

  3. reactions: Zero|PromiseReaction, argument: JSAny,

  4. reactionType: constexpr PromiseReactionType): void {

  5. // We need to reverse the {reactions} here, since we record them on the

  6. // JSPromise in the reverse order.

  7. let current = reactions;

  8. let reversed: Zero|PromiseReaction = kZero;

  9. // 链表反转

  10. while (true) {

  11. typeswitch (current) {

  12. case (Zero): {

  13. break;

  14. }

  15. case (currentReaction: PromiseReaction): {

  16. current = currentReaction.next;

  17. currentReaction.next = reversed;

  18. reversed = currentReaction;

  19. }

  20. }

  21. }

  22. // Morph the {reactions} into PromiseReactionJobTasks and push them

  23. // onto the microtask queue.

  24. current = reversed;

  25. // 链表反转后,调用 MorphAndEnqueuePromiseReaction,把链接中的每一项都进入 microtask 队列

  26. while (true) {

  27. typeswitch (current) {

  28. case (Zero): {

  29. break;

  30. }

  31. case (currentReaction: PromiseReaction): {

  32. current = currentReaction.next;

  33. MorphAndEnqueuePromiseReaction(currentReaction, argument, reactionType);

  34. }

  35. }

  36. }

  37. }

TriggerPromiseReactions 做了两件事:

反转 reactions 链表,前文有分析过 then 方法的实现,then 方法的参数最终存在链表中。最后被调用的 then 方法,它接收的参数被包装后会位于链表的头部,这不符合规范,所以需要反转

遍历 reactions 对象,将每个元素放入 microtask 队列

  1. const myPromise4 = new Promise((resolve, reject) => {

  2. setTimeout(_ => {

  3. resolve('my code delay 5000')

  4. }, 5e3)

  5. })

  6. myPromise4.then(result => {

  7. console.log('第 1 个 then')

  8. })

  9. myPromise4.then(result => {

  10. console.log('第 2 个 then')

  11. })

  12. // 打印顺序:

  13. // 第 1 个 then

  14. // 第 2 个 then

  15. // 如果把 TriggerPromiseReactions 中链表反转的代码注释掉,打印顺序为

  16. // 第 2 个 then

  17. // 第 1 个 then

resolve 的主要工作是遍历上节调用 then 方法时收集到的依赖,放入 microtask 队列中

原文地址:https://www.cnblogs.com/zhouyideboke/p/13825837.html