真正符合Promises/A+规范的Promise必须严格遵循四条铁律:三态单向流转(pending→fulfilled/rejected不可逆)、微任务调度(queueMicrotask优先)、then返回新实例、错误穿透(try/catch+reject传递)。

手动实现一个真正符合 Promises/A+ 规范、能支持全状态追溯和链式调用的 Promise,核心不在“能不能写出来”,而在于是否严格遵循三态单向流转、微任务调度、返回新实例、错误穿透这四条铁律。任何一处松动(比如用 setTimeout 替代 queueMicrotask,或在 then 里直接返回 this),都会导致链式中断、状态错乱或测试套件失败。
状态机必须封闭且不可逆,不能靠 if-else 模拟
Promise 的 PENDING → FULFILLED 或 REJECTED 是单向的,不是“当前状态是 pending 就执行 resolve”。很多手写版本只做 if (this.status === 'pending') { this.status = 'fulfilled' },看似合理,但漏掉了关键约束:一旦状态变更,后续所有 resolve/reject 调用必须被忽略。
实操建议:
- 用闭包变量(如
state、value、reason)保存内部状态,初始为'pending' -
resolve和reject函数开头必须加if (state !== 'pending') return,否则多次调用会污染状态 - 不要用
this.$$status这类可被外部篡改的属性,状态必须私有化 - executor 必须同步执行,但其内部调用
resolve/reject可以是异步的——这点常被误读为“executor 可以异步”,实际是禁止的
then 必须返回新 Promise,且返回值处理逻辑不能简化
then 返回的不是原实例,也不是任意对象,而是一个全新 MyPromise 实例,它的终态完全由上一个回调的执行结果决定。所谓“全状态追溯”,指的就是这个新 Promise 能准确承接前序的 fulfilled 值、rejected 原因,甚至嵌套的 thenable 对象。
实操建议:
- 每个
then调用都应创建并返回new MyPromise((resolve, reject) => {...}) - 必须封装
resolvePromise(promise2, x, resolve, reject)辅助函数,统一处理x类型:undefined、普通值、MyPromise实例、带then方法的对象(thenable) - 特别注意循环引用:
if (x === promise2)时,必须reject(new TypeError(...)),这是 Promises/A+ 第 2.3.1 条硬性要求 - 不要省略
try/catch:onFulfilled或onRejected抛错,必须立即用reject(e)向下游传递,这是错误穿透的基础
回调必须走微任务,queueMicrotask 是首选
规范明确要求:onFulfilled 和 onRejected 必须异步调用,且属于微任务(microtask),不是宏任务(macrotask)。用 setTimeout 或 setImmediate 会导致时序错误,Promise.resolve().then 是兼容方案,但有额外开销。
实操建议:
- Node.js 11+ 和现代浏览器均支持
queueMicrotask,优先使用它调度回调 - 若需兼容旧环境(如 IE),可用
Promise.resolve().then(() => {...})模拟,但别用setTimeout(..., 0) - 注意:executor 内部是同步执行的,但注册到
onFulfilledCallbacks数组里的回调,必须等 executor 执行完再进微任务队列 - 多个
then注册的回调要按注册顺序执行,所以onFulfilledCallbacks和onRejectedCallbacks必须是数组,不能只存一个函数
catch 是语法糖,但错误穿透机制不能靠它实现
catch(onRejected) 确实只是 then(null, onRejected) 的简写,但它背后依赖的是 then 内部对回调异常的捕获与重抛。很多人以为“写了 catch 就万事大吉”,其实只要 then 里没做 try/catch + reject(e),错误就无法向下传递。
实操建议:
-
catch方法体只需一行:return this.then(null, onRejected); - 真正关键的是
then内部对onFulfilled和onRejected的包裹逻辑:必须用try { const x = callback(value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } - 如果
onFulfilled里抛错,这个错误会被下一个then的onRejected捕获,不是因为catch,而是因为上一个then返回的新 Promise 已被reject - 不要在
catch回调里throw却不返回新 Promise,否则链式在此中断
最难的部分不是写代码,而是理解“每个 then 都在创建一个新 Promise,并把前一个的结果作为输入”这个抽象链条。状态、值、错误、微任务时机,四者耦合极紧,改一处就得通盘验证。Promises/A+ 测试套件跑不过,往往不是少写了一个 if,而是某个微任务没对齐、某个拒绝没透传、或者循环引用没拦截。

















