真实面经题目 · 原创解析

Promise 的状态流转和 then 链式调用机制是什么?

Promise 的核心不是“异步容器”这么简单,而是一套状态机、回调注册机制、返回值吸收规则和微任务调度规则的组合。面试回答要把 pending 到 fulfilled 或 rejected 的单向迁移、then 返回新 Promise、回调异步执行、值穿透、异常传播、thenable 展开和微任务边界串成完整链路。

出现于:阿里巴巴 · 前端

60 秒回答模板

Promise 有三种状态:pending、fulfilled、rejected。初始是 pending,一旦通过 resolve 进入 fulfilled,或通过 reject 进入 rejected,状态就不可再变。then 的作用不是直接修改原 Promise,而是给当前 Promise 注册成功和失败回调,并立即返回一个新的 Promise。当前 Promise 状态确定后,then 回调会被放入微任务队列异步执行。回调返回普通值,新 Promise fulfilled;抛出异常,新 Promise rejected;返回 Promise 或 thenable,则新 Promise 会采用它最终的状态。链式调用之所以成立,是因为每个 then 都返回全新的 Promise,后一个 then 监听的是前一个 then 返回的新 Promise,而不是最初那个 Promise。没有提供对应回调时会发生值穿透或错误穿透,使 fulfilled 值或 rejected 原因继续传递到后续链路。

考点 状态机模型
主线 状态与结果
易错点 把 Promise 理解成异步任务本身,忽略它其实是异…

深入解析

01

状态机模型

Promise 首先是一个一次性状态机。它只有 pending、fulfilled、rejected 三种状态,创建后默认处于 pending。pending 可以迁移到 fulfilled,也可以迁移到 rejected,但 fulfilled 和 rejected 都是终态,不能再回到 pending,也不能互相切换。这个不可逆约束保证异步结果只被结算一次,即使 executor 中多次调用 resolve、reject,真正生效的也只有第一次。

02

状态与结果

fulfilled 不只是一个状态,还会绑定一个成功值;rejected 也不只是失败状态,还会绑定一个拒因。后续 then、catch、finally 处理的是状态携带的值或错误原因。pending 阶段没有最终结果,只能收集回调;一旦状态落定,已注册回调会按注册顺序进入异步调度,之后再注册 then 也不会同步执行回调,而是同样进入微任务队列。

03

then 的职责

then 的职责可以拆成两部分:给当前 Promise 注册 onFulfilled、onRejected 两类反应函数,并返回一个新的 Promise。很多人误以为 then 返回的是原 Promise,实际上链式调用的关键正是每次 then 都创建新 Promise。当前回调的执行结果会被用来结算这个新 Promise,所以链上每一环都有自己的状态和值,后续 then 订阅的是上一环产生的新 Promise。

04

微任务边界

Promise 的 executor 会在创建 Promise 时同步执行,但 then 注册的回调不会在当前同步调用栈里立刻执行。即使 Promise 已经 fulfilled,then 中的 onFulfilled 也会被放进微任务队列,等当前同步代码执行完后再运行。链上每个 then 回调执行后,新 Promise 的结算又会触发下一批 reaction 进入微任务队列,从而形成按微任务推进的异步流水线。

05

返回值决定下游

then 回调返回什么,决定它返回的新 Promise 如何结算。返回普通值,包括 undefined,新 Promise 会以这个值 fulfilled;回调内部抛出异常,新 Promise 会以该异常 rejected;返回另一个 Promise,新 Promise 会等待它完成并采用它的最终状态和值。这个规则让链式调用既能传递普通计算结果,也能把新的异步过程串入同一条控制流。

06

Resolution Procedure

Promise Resolution Procedure 处理的是“用某个值 x 去 resolve 一个新 Promise”时应该发生什么。如果 x 是普通值,就直接 fulfilled;如果 x 是同一个 Promise,需要拒绝以避免循环;如果 x 是 Promise 或 thenable,就读取它的 then,并按 thenable 协议尝试接管它的最终状态。这个过程保证不同实现或类 Promise 对象也能被 Promise 链正确展开。

07

值穿透与错误穿透

then 的两个参数都不是必填。如果 fulfilled 链路上没有提供 onFulfilled,成功值会原样传给下一个 then;如果 rejected 链路上没有提供 onRejected,拒因会继续向后传播,直到遇到 catch 或带失败回调的 then。值穿透解释了为什么空 then 不会吞掉结果,也解释了 catch 可以放在链条后部统一处理错误。

08

异常传播

then 回调内部的同步异常会被 Promise 捕获,并转成返回的新 Promise 的 rejected 状态。之后如果后续 then 没有失败处理函数,错误会继续穿透;如果某个 catch 或 onRejected 返回普通值,链路会从 rejected 恢复到 fulfilled。也就是说错误不是全局外抛,而是在 Promise 链的状态系统中流转。

易错点

  • 把 Promise 理解成异步任务本身,忽略它其实是异步结果的状态管理和回调衔接机制。
  • 认为 then 会修改原 Promise 的值,忽略 then 返回的是一个新的 Promise。
  • 认为 Promise 已经成功时 then 回调会同步执行,忽略回调始终通过微任务调度。
  • 把 resolve 等同于立即成功,忽略 resolve 会解析 Promise 或 thenable 并采用最终状态。
  • 忽略值穿透和错误穿透,无法解释空 then、不带失败回调和链尾 catch 的行为。
  • 把异常传播说成普通 try-catch 外抛,忽略异常会转成新 Promise 的 rejection。

面试官追问

为什么 then 要返回一个新的 Promise?

因为每个 then 回调都可能把上游结果转换成新的值、抛出新的错误,或返回新的异步任务。只有返回新 Promise,才能把这一环的处理结果作为下游输入,并让每一环都有独立状态。

resolve 一个 Promise 和 fulfilled 是一回事吗?

不完全是。resolve 表示启动解析过程,如果传入普通值会 fulfilled;如果传入 Promise 或 thenable,则要等待并采用它的最终状态。因此 resolve 之后未必立即 fulfilled,也可能最终 rejected。

为什么已成功的 Promise 再 then 仍是异步?

这是 Promise 调度规则的一部分,用来保证 then 回调不会打断当前同步调用栈,使执行顺序稳定可预测。无论注册 then 时 Promise 是否已结算,reaction 都通过微任务执行。

catch 后面的 then 为什么可能走成功分支?

catch 本质上是 then(null, onRejected)。如果 catch 回调处理了错误并返回普通值,catch 返回的新 Promise 会变成 fulfilled,后续 then 的成功回调就会执行。只有再次抛错或返回 rejected Promise,失败才会继续传播。

thenable 和 Promise 有什么区别?

Promise 是符合规范的对象,thenable 只是拥有 then 方法的对象或函数。Promise 解析过程会尝试接管 thenable 的状态,但 thenable 本身未必具备完整 Promise 行为,因此规范要处理多次调用、读取 then 抛错等边界。