必须用 queuemicrotask 或 promise.resolve().then 实现微任务调度,状态机与 executor 同步执行需严格隔离,resolve/reject 内部必须包裹微任务执行回调;then 返回新 promise 并支持值穿透;resolvepromise 是唯一终态决策入口,须处理自引用、thenable、错误捕获等所有边界场景。

必须用 queueMicrotask 或 Promise.resolve().then 实现微任务调度,否则无法通过 Promises/A+ 测试套件中关于“异步执行回调”的关键用例(如 `2.2.4` 和 `3.1`)。
状态机与 executor 同步执行必须严格隔离
构造函数内必须立即同步调用 executor,但 resolve 和 reject 内部不能直接执行 onFulfilled / onRejected 回调 —— 即使状态已是 fulfilled,也必须包一层微任务。
- 错误做法:在
resolve中直接调用onFulfilled(value),会导致同步执行,违反规范 2.2.4 “onFulfilled必须异步调用” - 正确做法:统一走
queueMicrotask(() => { /* 执行回调 + resolvePromise */ }) - 兼容性兜底:若环境不支持
queueMicrotask(如旧版 Safari),可用Promise.resolve().then替代,但注意这会额外创建一个 Promise,轻微开销
then 返回新 Promise 且必须支持值穿透
then 方法返回的不是原实例,而是一个全新 MyPromise 实例;当传入的 onFulfilled 或 onRejected 未提供时,必须透传上一级的终值或拒因 —— 这是错误冒泡和链式调用的基础。
- 例如:
p.then().then(x => x * 2)中第一个then()没传参数,必须把p的终值原样传给下一个then - 实现上:对
onFulfilled做默认赋值onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value,onRejected同理 - 若不处理透传,
Promise.resolve(42).then().then(console.log)就不会输出42
resolvePromise 是唯一能决定 promise2 走 resolve 还是 reject 的入口
这个函数处理 then 回调的返回值 x,它决定了 promise2(即 then 返回的新 Promise)的状态。所有分支最终都必须只调用一次 resolve2 或 reject2,且禁止重复调用。
- 最常见坑:没判断
x === promise2,导致自引用死循环,抛出TypeError: Chaining cycle detected for promise - 关键逻辑:先检查
x是否为null或原始值(typeof x === 'object' || typeof x === 'function'),再尝试取x.then;取值报错 → 立即reject2(err);then是函数 → 用called标志位确保只调用一次resolveX或rejectX - 注意:即使
x是其他规范的 Promise(如 jQuery Deferred),只要它有可调用的then方法,就必须按 thenable 处理
错误捕获必须覆盖 executor 和 resolve/reject 调用链
executor 抛错、resolve / reject 内部取 x.then 抛错、甚至 onFulfilled 回调本身抛错 —— 所有这些错误都必须被捕捉并导向 reject2,否则链会中断。
- executor 错误:构造函数内用
try/catch包裹executor(resolve, reject),出错则reject(err) - 回调错误:在
queueMicrotask中执行onFulfilled后,必须再套一层try/catch,捕获后调用reject2(err) - 忽略这点会导致
Promise.reject('x').then(null, () => {}).then(() => console.log('never'))中最后一段不执行 —— 因为中间没有错误传递机制
真正难的不是写完代码,而是让 resolvePromise 在所有嵌套 thenable、自引用、空值、同步 throw、异步 reject 场景下都只触发一次终态 —— 这个控制点一旦松动,整个链就不可靠。










