手动实现 Promise 需在构造函数中维护 pending/fulfilled/rejected 三种状态、value/reason 值及 onFulfilledCallbacks/onRejectedCallbacks 回调数组;.then 必返回新 Promise 并通过 resolvePromise 统一处理返回值;.catch 是 .then(null, onRejected) 的语法糖;所有回调需用 queueMicrotask 或 Promise.resolve().then 做微任务调度以符合规范。
手动实现 Promise,核心是管理三个状态:pending、fulfilled、rejected,以及在状态变更时触发的回调队列。不能只存一个回调,因为 .then 可能被多次调用,每次都要排队等执行。
构造函数接收一个执行器函数 executor,它立即同步执行,并传入 resolve 和 reject 两个函数。这两个函数不能直接返回值,而是用来改变实例内部状态并触发对应队列。
resolve 只在状态为 pending 时才生效,把状态设为 fulfilled,保存 value,然后遍历执行 onFulfilledCallbacks
reject 同理,设为 rejected,保存 reason,执行 onRejectedCallbacks
.then 的注册.then 必须返回一个新的 Promise 实例,这是链式调用的基础。否则 promise.then(...).then(...) 第二个 .then 就会报错 —— 因为第一个 .then 如果返回普通值或没返回,就不是 Promise。
这个新 Promise 的状态,取决于上一个 .then 里回调函数的执行结果:
Promise(比如 return new MyPromise(...)),新 Promise 应该等待它结束,并继承其状态和值Promise 立即变为 rejected
undefined),新 Promise 变为 fulfilled,值就是该返回值所以 .then 内部需要调用一个辅助函数(常叫 resolvePromise)来统一处理返回值,避免重复逻辑。
.catch 本身不参与状态流转,它只是把 onRejected 回调透传给 .then 的第二个参数。所以最简实现就是:catch(onRejected) { return this.then(null, onRejected); }
但要注意:如果 .then 的 onFulfilled 回调里抛错,这个错误会被后续的 .catch 捕获,因为错误会向下穿透到最近的 onRejected 处理器 —— 这依赖于 .then 返回的新 Promise 在执行回调出错时自动 reject。
.catch 里重新 throw 错误却不返回新 Promise,否则链会断.catch 回调自己返回值(比如修复后返回默认数据),那后续 .then 会收到这个值,状态是 fulfilled
Promise.prototype.catch = function(onRejected) { return this.then(undefined, onRejected); }; 是合法且等价的写法原生 Promise 的 then 回调属于微任务(microtask),总在当前宏任务结束后、下次渲染前执行。手动实现时若用 setTimeout,就变成了宏任务,行为不一致。
可以用 queueMicrotask(现代环境)或降级用 Promise.resolve().then 模拟微任务调度:
function nextTick(fn) { if (typeof queueMicrotask === 'function') { queueMicrotask(fn); } else { Promise.resolve().then(fn); }}
所有状态变更后的回调执行(比如 resolve 后触发 onFulfilledCallbacks)都必须包裹在 nextTick 里,否则同步执行会导致顺序错乱,比如:
.then 注册后,它们的回调应按注册顺序在下一个微任务中执行,而非立即交错运行微任务调度这一步最容易被忽略,但它决定了你的实现是否符合 Promise/A+ 规范的行为预期。