PromiseResolveThenableJobTask 微任务是怎么被执行的

背景

要理解 NewPromiseResolveThenableJobTask 这个微任务的作用,需要先回到它的创建场景 :当一个 Promise 的 then 回调(或其他 Promise 相关回调)返回了一个 可 thenable 对象 (通常是另一个 Promise,但也包括自定义的、有 then 方法的对象)时,JS 引擎会创建这个微任务,本质是为了 确保 "返回的 thenable 对象" 的状态完全确定后,再继续执行后续的 Promise 链

如下代码,后续讨论依托于此段代码展开:

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

先明确前提:为什么需要这个微任务?

Promise 链的核心规则是 "后续的 then 回调,必须等待前一个 then 回调的 "返回值" 状态确定后才执行 "。如果前一个 then 回调返回的是一个普通值(比如 4、'a'),那很简单:直接把这个普通值作为下一个 Promise 的 "成功值",立刻触发下一个 then 的微任务。

但如果返回的是一个Promise(或可 thenable 对象) (比如代码中 return Promise.resolve(4)),情况就复杂了:这个返回的 Promise 本身是 "异步状态"(即使是 Promise.resolve(4) 这种 "立即成功" 的,也不是同步执行),必须等待它的 then 回调执行完(状态完全确定),才能把它的结果传给下一个 then

这时候,NewPromiseResolveThenableJobTask 就是用来干这个 "等待并传递结果" 的活的。

NewPromiseResolveThenableJobTask 微任务具体做什么?

它的执行逻辑可以拆解为 3 个核心步骤,本质是 "代理执行返回的 thenable 对象的 then 方法,并把结果同步给当前 Promise 链":

1. 先明确这个微任务的 "3 个输入参数"

在创建这个微任务时,引擎会传入 3 个关键信息(对应 ECMA 规范的定义: #sec-newpromiserelovethenablejobtask):

  • promiseToResolve:当前 Promise 链中的 "中间 Promise"(即前一个 then 执行后返回的新 Promise,后续的 then 都是挂在这个 Promise 上的)。
  • thenable:前一个 then 回调返回的 "可 thenable 对象"(比如代码中 return Promise.resolve(4) 里的这个 Promise)。
  • thenthenable 对象自身的 then 方法(比如 Promise 原型上的 Promise.prototype.then)。
js 复制代码
    const promiseToResolve = Promise.resolve().then(() => { 
        return [[thenable 对象]]: { 
            [[then 方法]]: (onFulfilled, onRejected) => {}
        }
     })

2. 微任务执行的核心逻辑

当这个微任务被执行时,会干两件关键的事:

第一步:调用 thenablethen 方法,绑定 "结果传递回调"

微任务会主动调用 thenable.then(...),并传入两个回调函数(相当于 "代理回调"):

  • 成功回调(onFulfilled):如果 thenable 成功(比如 Promise.resolve(4) 成功),就会触发这个回调,把 thenable 的成功值(比如 4)传给 promiseToResolve,让 promiseToResolve 也变成 "成功状态",并把 4 作为它的成功值。
  • 失败回调(onRejected):如果 thenable 失败(比如 Promise.reject(5)),就会触发这个回调,把 thenable 的失败原因传给 promiseToResolve,让 promiseToResolve 变成 "失败状态"。
第二步:触发后续的 Promise 链

一旦 promiseToResolve 的状态被确定(成功 / 失败),引擎就会为 promiseToResolve 上挂载的后续 then 回调(比如代码中第二个 then((res) => console.log(res)))创建新的微任务,把刚才传递过来的结果(比如 4)传给这个后续回调,让 Promise 链继续执行。

结合代码,看这个微任务的具体作用

javascript 复制代码
Promise.resolve() // 1. 初始 Promise(成功状态,值为 undefined)
  .then(() => { // 2. 第一个 then 回调(cb1)
    console.log(0); // 3. 同步打印 0
    return Promise.resolve(4); // 4. 返回一个新的 Promise(记为 P4)
  })
  .then((res) => { // 5. 第二个 then 回调(cb2,挂在 "中间 Promise":P_mid 上)
    console.log(res); // 6. 最终打印 4
  });

这里 NewPromiseResolveThenableJobTask 的执行过程完全贴合上面的逻辑:

  1. 执行 cb1 时,返回了 Promise.resolve(4)(即 thenable = P4),引擎创建 NewPromiseResolveThenableJobTask 微任务,传入:

    • promiseToResolve = P_mid(第一个 then 返回的中间 Promise,cb2 挂在它上面)
    • thenable = P4
    • then = P4.then(即 Promise 原型的 then 方法)。
  2. 当这个微任务执行时:

    • 调用 P4.then(成功回调, 失败回调):因为 P4Promise.resolve(4)(已成功),触发 PromiseResolveThenableJobTask的执行流程(重点)。
    • "成功回调" 把 4 传给 P_mid,让 P_mid 变成 "成功状态",值为 4。
    • 引擎为 P_mid 上的 cb2 创建新微任务,后续执行这个微任务时,就会把 4 传给 cb2,打印出 4

重点核心(PromiseResolveThenableJobTask被执行):

PromiseResolveThenableJobTask 的被执行过程,esma 规范 的描述

其实它规定了这个 PromiseResolveThenableJobTask 要作为异步任务去执行(加入任务队列);

v8::builtins::promise-resolve.tq 的处理是一样的。v8 把 它加入了 微任务队列

但是,这个任务后续的被执行,就没有说了,规范上我并没有找到;

但是,看了 PromiseResolveThenableJobTaskv8 具体实现, 它分了两种情况来处理的:

  1. thenable对象Promise对象时;执行 PerformPromiseThen的实现方式;
    • 如果 promise 是 pendding 态,会创建一个 NewPromiseReaction 任务, 等待 promise 兑现;
    • 如果 promise 是 fulfiled|rejected, 则会创建一个微任务,加入到微任务队列,等待执行;
  2. thenable对象非Promise 对象时;直接走 resolve function 逻辑;
    • 直接调用 resolve(结果),兑现 promise;

如图(PromiseResolveThenableJob):

如图(PerformPromiseThenImpl):

也就是说,以下两段代码输出的结果是不一样的:

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);
  });
  
// 输出:0,1,2,3,4,5 
js 复制代码
Promise.resolve()
  .then(() => {
    console.log(0);
    return {
      then(onFulfilled, onRejected) {
        onFulfilled(4);
      },
    };
  })
  .then((res) => {
    console.log(res);
  });

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

// 输出:0,1,2,4,3,5

一句话总结

NewPromiseResolveThenableJobTask 就是一个 "结果传递代理微任务":专门处理 "then 回调返回 thenable 对象" 的场景,确保这个 thenable 对象的状态确定后,把它的结果同步给当前 Promise 链的中间 Promise,再触发后续的 then 回调,保证 Promise 链的顺序和状态正确性。

注意的点是:

thenable对象是 Promise对象 和 普通包含实现 then 方法的对象,PromiseResolveThenableJobTask 的处理方式是不一样的;

相关推荐
五月君_4 小时前
炸裂!Claude Opus 4.6 与 GPT-5.3 同日发布:前端人的“自动驾驶“时刻到了?
前端·gpt
Mr Xu_4 小时前
前端开发中CSS代码的优化与复用:从公共样式提取到CSS变量的最佳实践
前端·css
鹏北海-RemHusband5 小时前
从零到一:基于 micro-app 的企业级微前端模板完整实现指南
前端·微服务·架构
LYFlied5 小时前
AI大时代下前端跨端解决方案的现状与演进路径
前端·人工智能
光影少年5 小时前
AI 前端 / 高级前端
前端·人工智能·状态模式
一位搞嵌入式的 genius5 小时前
深入 JavaScript 函数式编程:从基础到实战(含面试题解析)
前端·javascript·函数式
anOnion5 小时前
构建无障碍组件之Alert Dialog Pattern
前端·html·交互设计
choke2335 小时前
[特殊字符] Python 文件与路径操作
java·前端·javascript
云飞云共享云桌面5 小时前
高性能图形工作站的资源如何共享给10个SolidWorks研发设计用
linux·运维·服务器·前端·网络·数据库·人工智能
Deng9452013145 小时前
Vue + Flask 前后端分离项目实战:从零搭建一个完整博客系统
前端·vue.js·flask