Promise状态一旦从pending变为fulfilled或rejected便不可逆,首次resolve或reject调用生效,后续调用静默忽略;该规则由ECMAScript规范定义,V8等引擎仅忠实实现,而非裁决胜者。

Promise一旦进入fulfilled或rejected状态,就不可逆,后续的resolve或reject调用会被忽略。V8引擎不“决定胜出者”,而是遵循规范:**首次状态变更生效,其余调用无效**。
状态只能变更一次
根据ECMAScript规范,Promise内部有一个[[PromiseState]]内部槽,初始为"pending"。当第一次调用resolve或reject时,它会尝试将状态设为"fulfilled"或"rejected"。一旦成功变更,后续所有resolve/reject调用都不再改变状态,也不抛出错误,只是静默忽略。
- 即使在同一个executor中同时写resolve(1)和reject('err'),先执行的那个生效
- 执行顺序取决于代码顺序,不是竞态或引擎随机选择
- V8不介入“裁决”,只是忠实实现规范中的状态机逻辑
常见误解澄清
有人以为resolve和reject像race一样竞争,实际并非如此。它们不是异步任务,而是同步函数调用;executor本身是同步执行的,状态变更也是同步完成的。
- 例如:
new Promise((r, j) => { r(1); j('err'); })→ 状态为fulfilled,值为1 - 反过来:
new Promise((r, j) => { j('err'); r(1); })→ 状态为rejected,理由为'err' - 即便在微任务中延迟调用(如
setTimeout(r, 0)),只要状态仍是pending,仍可生效;但一旦状态已定,再调也无效
为什么设计成这样?
这种“不可变状态”设计是为了保证Promise的可靠性与可预测性。避免因多次调用导致行为模糊、调试困难或资源泄漏。
立即学习“Java免费学习笔记(深入)”;
- 开发者无需担心回调被多次触发(.then/.catch只会执行一次)
- 链式调用(.then().catch())能稳定传递结果或错误
- 符合函数式编程中“单一可信源”的思想
不复杂但容易忽略:关键不在引擎“选谁”,而在Promise的状态机规则本身——先到先得,且仅此一次。


















