PromiseResolveThenableJobTask 的在Promise中的使用

问题:

最近看到一个有意思的面试题:

js 复制代码
Promise.resolve()
  .then(() => {
    console.log(0);
    return Promise.resolve(4);
  })
  .then((res) => {
    console.log(res);
  });

Promise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .then(() => {
    console.log(3);
  })
  .then(() => {
    console.log(5);
  });

结论:

最终 console 输出顺序: 0, 1, 2, 3, 4, 5

原因解释

重点:Promise的处理 与 微任务队列:

解析步骤拆解:

  1. 脚本同步阶段:
  • 两个 Promise.resolve() 都是已解决的,同步地注册各自的第一个 .then 的 reaction。它们会分别创建"返回的 promise"(记为 A_return、B_return),并把对应的 PromiseFulfillReactionJobTask(执行第一个 then 的回调)按注册顺序推入微任务队列。
    • 初始 微任务队列 (FIFO): A (第一个链的第1个 then 回调), B (第二个链的第1个 then 回调)
  1. 执行微任务(按队列):

    1. 运行 A:

      • 执行回调,打印 0
      • 回调返回 Promise.resolve(4) ---> 一个 thenable(JSPromise)
      • 为把 A_return 与该 thenable 关联,V8 创建并入队一个 PromiseResolveThenableJobTask(见 src/objects/promise.tqPromiseResolveThenableJobTask 是 Microtask,携带 promise_to_resolvethenablethen 等字段),用于在稍后调用 thenable.then(resolve, reject)
      • 此时,微任务队列B, PRJ(PRJ = PromiseResolveThenableJobTask)
    2. 运行 B:

    • 打印 1 ,返回 undefined,导致 B_return 被 fulfill 并把 B_return.then(...)(即后续的 handler2)对应的 reaction 入队。
      • 此时,微任务队列PRJ, B2
    1. 运行 PRJ(PromiseResolveThenableJobTask):
    • 它会执行 thenable.then(resolve, reject)。因为 thenable 是已解决的 Promise.resolve(4),调用 then 会把一个将调用 resolve(4) 的 reaction 入队(记为 TJ)。

    • PRJ 本身不直接调用 resolve(4),只执行 then 的调用来把 thenable 的 reaction 安排为微任务。

    • 此时,微任务队列B2, TJ

    1. 运行 B2(第二链的第二个 then):
    • 打印 2 ,返回 undefined,注册并入队 B3(下一 then 的 reaction)。

    • 此时,微任务队列TJ, B3

    1. 运行 TJ(thenable 的反应,执行 resolve(4)):
    • resolve(4) 满足了 A_return,把 A_return 的后续 reaction(即 A 的第二个 then,也就是打印 res 的回调 A2)入队。

    • 此时,微任务队列B3, A2

    1. 运行 B3:
    • 打印 3,入队 B4(最后一个 then 的 reaction)。

    • 此时,微任务队列A2, B4

    1. 运行 A2:打印 4
    2. 运行 B4:打印 5

要点总结:

  • then 回调返回一个 thenable(这里是 Promise.resolve(4))时,规范要求通过一个 PromiseResolveThenableJob(微任务)去"取得 then 并调用它",因此会生成一个专门的微任务(PRJ)来调用 then。该 PRJ 会排在当时队列的尾部。
  • thenable.then(resolve, reject) 本身再产生一个微任务(TJ)来真正调用 resolve(4)。因此 A_return 的 fulfill(A2)要等到 TJ 执行后才会被入队并最终运行。
  • 因为多个微任务在运行期间会继续入队(并保持 FIFO),所以第二条 promise 链的中间 then(打印 2、3)会在 A_return 的最终 reaction(打印 4)之前被调度并打印。
相关推荐
海兰7 小时前
【应用】基于 Next.js 16 + Python mplfinance的金融K线图与技术指标可视化平台(三)
javascript·python·金融
小磊哥er9 小时前
深入解构Claude Code - 第 7 篇 · 连接外面的世界
javascript·ai编程
小磊哥er9 小时前
深入解构Claude Code - 第 6 篇 · 终端里的图形界面
javascript·ai编程
海兰9 小时前
【应用】基于 Next.js 16 + Python mplfinance的金融K线图与技术指标可视化平台(一)
javascript·python·金融
可乐鸡翅yeah_11 小时前
hls.js 内存泄漏实战排查,直播 M3U8 长时间播放异常定位方案
开发语言·javascript·python·django·ecmascript·m3u8·m3u8在线
可乐鸡翅yeah_11 小时前
第三方接口对接 M3U8 流媒体排坑实战,解决外部服务商流兼容难题
前端·javascript·python·django·html·m3u8·m3u8在线
默_笙11 小时前
🏠 「LLM Notes」:我用 Next.js + Redis 给自己造了个笔记博客
前端·javascript
ruanCat13 小时前
Prettier 明明执行成功,Markdown 为什么还是没变?一次插件误诊复盘
前端·javascript·node.js
软件开发技术深度爱好者13 小时前
在线学习实践 TypeScript 的官方Playground(演练场)使用介绍
javascript·学习·typescript
小磊哥er15 小时前
深入解构Claude Code - 第 5 篇 · 每次提问都给 AI 塞了什么资料
javascript·ai编程