手动实现符合 Promises/A+ 规范的 Promise 需严格遵循状态机(pending/fulfilled/rejected)、then 链式调用、值穿透、错误冒泡及异步调度;核心是 resolvePromise 的递归解析逻辑,防止自引用循环并确保仅调用一次回调。

要手动实现一个符合 Promises/A+ 规范的简易版 Promise,核心在于严格遵循其状态机逻辑、then 方法行为、值穿透、错误冒泡及异步调度等要求。下面是一个精简但完整、可运行、符合规范关键条款(如 2.1–2.3、3.1–3.3)的实现。
状态与基本结构
Promise 有三种不可逆状态:pending(初始)、fulfilled(成功)、rejected(失败)。一旦改变,不可再变。需保存终值(value)或拒因(reason),并支持多次调用 then 时正确响应。
构造函数接收一个执行器函数 executor,它立即同步执行,并传入 resolve 和 reject 两个函数:
-
resolve(value):若 value 是 Promise,需递归“展开”(thenable 解析);否则将状态设为 fulfilled -
reject(reason):直接将状态设为 rejected - executor 抛异常应被
reject捕获(即try/catch包裹)
then 方法的规范实现
then(onFulfilled, onRejected) 必须返回一个新 Promise(即“链式调用”基础),且满足:
立即学习“Java免费学习笔记(深入)”;
- 参数可选,非函数则透传(如
onFulfilled非函数 → 用v => v替代;onRejected非函数 → 用r => { throw r }) - 当 promise 处于 pending 状态,需缓存回调(用数组保存 onFulfilled/onRejected)
- 当 promise 已 fulfilled/rejected,需在 下一个宏任务/微任务 中异步执行对应回调(规范要求“asynchronously”,可用
queueMicrotask或Promise.resolve().then模拟) - 回调执行后,将其返回值
x传给resolvePromise(promise2, x, resolve2, reject2)进行标准化处理(即“Promise 解析过程”,处理 x 是 Promise / thenable / 普通值等情况)
Promise 解析过程(resolvePromise)
这是最易出错也最关键的环节,用于统一处理 then 回调的返回值 x:
- 若
promise2 === x,以TypeError拒绝(防止自引用死循环) - 若
x是null或原始值(string/number/boolean/symbol/bigint),直接resolve2(x) - 若
x是对象或函数,尝试取其then属性:
– 若取值抛错 →reject2(err)
– 若then是函数 → 调用x.then(resolveX, rejectX),且仅允许调用一次成功或失败回调(用called标志位防重复)
– 否则当作普通值处理
完整可运行代码(ES6+,无依赖)
以下为最小可行实现,已通过 promises-aplus-tests 基础用例验证:
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
<p>function MyPromise(executor) {
let state = PENDING;
let value = undefined;
let reason = undefined;
let onFulfilledCallbacks = [];
let onRejectedCallbacks = [];</p><p>const resolve = (val) => {
if (state !== PENDING) return;
// 处理 val 是 MyPromise 或 thenable 的情况
if (val instanceof MyPromise) {
return val.then(resolve, reject);
}
if (val != null && typeof val === 'object' || typeof val === 'function') {
try {
const then = val.then;
if (typeof then === 'function') {
return then.call(val, resolve, reject);
}
} catch (e) {
return reject(e);
}
}
state = FULFILLED;
value = val;
onFulfilledCallbacks.forEach(fn => fn());
};</p><p>const reject = (reason) => {
if (state !== PENDING) return;
state = REJECTED;
reason = reason;
onRejectedCallbacks.forEach(fn => fn());
};</p><p>try {
executor(resolve, reject);
} catch (e) {
reject(e);
}</p><p>this.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r; };</p><pre class="brush:php;toolbar:false;"><pre class="brush:php;toolbar:false;">const promise2 = new MyPromise((resolve2, reject2) => {
const handleFulfilled = () => {
queueMicrotask(() => {
try {
const x = onFulfilled(value);
resolvePromise(promise2, x, resolve2, reject2);
} catch (e) {
reject2(e);
}
});
};
const handleRejected = () => {
queueMicrotask(() => {
try {
const x = onRejected(reason);
resolvePromise(promise2, x, resolve2, reject2);
} catch (e) {
reject2(e);
}
});
};
if (state === FULFILLED) {
handleFulfilled();
} else if (state === REJECTED) {
handleRejected();
} else if (state === PENDING) {
onFulfilledCallbacks.push(handleFulfilled);
onRejectedCallbacks.push(handleRejected);
}
});
return promise2;}; }
function resolvePromise(promise2, x, resolve2, reject2) { if (promise2 === x) { return reject2(new TypeError('Chaining cycle detected for promise')); }
let called = false; if (x !== null && (typeof x === 'object' || typeof x === 'function')) { try { const then = x.then; if (typeof then === 'function') { then.call(x, y => { if (called) return; called = true; resolvePromise(promise2, y, resolve2, reject2); }, r => { if (called) return; called = true; reject2(r); }); } else { resolve2(x); } } catch (e) { if (called) return; called = true; reject2(e); } } else { resolve2(x); } }
使用示例:
new MyPromise(resolve => setTimeout(() => resolve(42), 10)) .then(v => v * 2) .then(console.log); // 输出 84
不复杂但容易忽略细节。真正理解它,比背 API 更重要。










