Back to listPromise A+ 规范实现
Site Owner
Published on 2026-05-21
完整实现Promise A+规范,涵盖状态机转换、then链值传递与错误传递、决议程序递归平铺算法及Promise.all/race/any实现
Promise A+ 规范实现
Promise 是 JavaScript 异步编程的基石。Promises/A+ 规范定义了 then 方法的行为和状态转换规则,ECMAScript 规范在此基础上扩展了 catch、finally、all、race 等静态方法。本文将从零实现一个完全符合 A+ 规范的 Promise,深入分析 V8 中 Promise 的微任务调度机制,并对比原生 Promise 与手写版本的性能差异。
目录
- Promise 状态机模型
- 从零实现 Promise A+
- then 的链式调用与值穿透
- Resolution Procedure 解析算法
- 静态方法实现
- V8 中的 Promise 优化
- 错误处理与未捕获拒绝
- 实战案例
- 深度追问
- 总结表格
1. Promise 状态机模型
resolve(value)
┌──────────────────────┐
│ ▼
┌───────┐ ┌──────────┐
│Pending│ │Fulfilled │ → 不可再变
└───┬───┘ └──────────┘
│
│ reject(reason)
│ ┌──────────┐
└───────────────►│ Rejected │ → 不可再变
└──────────┘
规范要求(A+ §2.1):
- pending 可转为 fulfilled 或 rejected
手写Promise A+规范完整实现—状态机、链式调用与决议程序|新宇宙博客fulfilled/rejected 后状态不可变fulfilled 必须有一个不可变的 valuerejected 必须有一个不可变的 reason
2. 从零实现 Promise A+
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
#state = PENDING;
#value = undefined;
#handlers = [];
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver must be a function');
}
const resolve = (value) => {
this.#transition(FULFILLED, value);
};
const reject = (reason) => {
this.#transition(REJECTED, reason);
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
#transition(state, value) {
if (this.#state !== PENDING) return;
this.#state = state;
this.#value = value;
this.#executeHandlers();
}
#executeHandlers() {
if (this.#state === PENDING) return;
queueMicrotask(() => {
while (this.#handlers.length) {
const handler = this.#handlers.shift();
this.#handle(handler);
}
});
}
#handle({ onFulfilled, onRejected, resolve, reject }) {
const callback = this.#state === FULFILLED ? onFulfilled : onRejected;
if (typeof callback !== 'function') {
if (this.#state === FULFILLED) {
resolve(this.#value);
} else {
reject(this.#value);
}
return;
}
try {
const result = callback(this.#value);
resolvePromise(resolve, reject, result);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handlers.push({ onFulfilled, onRejected, resolve, reject });
this.#executeHandlers();
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => MyPromise.resolve(onFinally()).then(() => value),
reason => MyPromise.resolve(onFinally()).then(() => { throw reason; })
);
}
}
3. then 的链式调用与值穿透
MyPromise.resolve(1)
.then(2)
.then(null)
.then(v => {
console.log(v);
});
MyPromise.resolve('start')
.then(v => v + ' → step1')
.then(v => v + ' → step2')
.then(v => {
console.log(v);
});
then 返回 Promise 的情况
MyPromise.resolve(1)
.then(v => {
return new MyPromise((resolve) => {
setTimeout(() => resolve(v * 10), 100);
});
})
.then(v => {
console.log(v);
});
4. Resolution Procedure 解析算法
A+ §2.3 定义了 Promise Resolution Procedure [[Resolve]](promise, x):
function resolvePromise(resolve, reject, x) {
if (x instanceof MyPromise) {
x.then(resolve, reject);
return;
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called = false;
try {
const then = x.then;
if (typeof then === 'function') {
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(resolve, reject, y);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (err) {
if (!called) {
reject(err);
}
}
} else {
resolve(x);
}
}
5. 静态方法实现
MyPromise.resolve = function(value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value));
};
MyPromise.reject = function(reason) {
return new MyPromise((_, reject) => reject(reason));
};
MyPromise.all = function(promises) {
return new MyPromise((resolve, reject) => {
const results = [];
let remaining = 0;
const iterable = [...promises];
if (iterable.length === 0) {
resolve(results);
return;
}
iterable.forEach((promise, index) => {
remaining++;
MyPromise.resolve(promise).then(
value => {
results[index] = value;
if (--remaining === 0) resolve(results);
},
reject
);
});
});
};
MyPromise.race = function(promises) {
return new MyPromise((resolve, reject) => {
for (const promise of promises) {
MyPromise.resolve(promise).then(resolve, reject);
}
});
};
MyPromise.allSettled = function(promises) {
return new MyPromise((resolve) => {
const results = [];
let remaining = 0;
const iterable = [...promises];
if (iterable.length === 0) {
resolve(results);
return;
}
iterable.forEach((promise, index) => {
remaining++;
MyPromise.resolve(promise).then(
value => {
results[index] = { status: 'fulfilled', value };
if (--remaining === 0) resolve(results);
},
reason => {
results[index] = { status: 'rejected', reason };
if (--remaining === 0) resolve(results);
}
);
});
});
};
MyPromise.any = function(promises) {
return new MyPromise((resolve, reject) => {
const errors = [];
let remaining = 0;
const iterable = [...promises];
if (iterable.length === 0) {
reject(new AggregateError([], 'All promises were rejected'));
return;
}
iterable.forEach((promise, index) => {
remaining++;
MyPromise.resolve(promise).then(
resolve,
reason => {
errors[index] = reason;
if (--remaining === 0) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
});
});
};
6. V8 中的 Promise 优化
PromiseHook 与 async_hooks
const { createHook, executionAsyncId, triggerAsyncId } = require('async_hooks');
const hook = createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (type === 'PROMISE') {
console.log(`Promise ${asyncId} created by ${triggerAsyncId}`);
}
},
before(asyncId) { },
after(asyncId) { },
destroy(asyncId) { }
});
hook.enable();
V8 的零成本 await 优化
async function optimized() {
const p = Promise.resolve(42);
return await p;
}
async function notOptimized() {
const thenable = { then(resolve) { resolve(42); } };
return await thenable;
}
7. 错误处理与未捕获拒绝
window.addEventListener('unhandledrejection', event => {
console.error('Unhandled:', event.reason);
event.preventDefault();
});
window.addEventListener('rejectionhandled', event => {
console.log('Late handled:', event.reason);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled:', reason);
});
process.on('rejectionHandled', (promise) => {
console.log('Late handled');
});
错误恢复模式
function fetchWithRetry(url, options = { retries: 3, delay: 1000 }) {
return new Promise((resolve, reject) => {
let attempts = 0;
function attempt() {
fetch(url)
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(resolve)
.catch(err => {
attempts++;
if (attempts >= options.retries) {
reject(new Error(`Failed after ${attempts} attempts: ${err.message}`));
} else {
setTimeout(attempt, options.delay * Math.pow(2, attempts - 1));
}
});
}
attempt();
});
}
8. 实战案例
实战案例 1:并发控制器
class PromisePool {
#concurrency;
#running = 0;
#queue = [];
constructor(concurrency = 5) {
this.#concurrency = concurrency;
}
async add(taskFn) {
if (this.#running >= this.#concurrency) {
await new Promise(resolve => this.#queue.push(resolve));
}
this.#running++;
try {
return await taskFn();
} finally {
this.#running--;
if (this.#queue.length > 0) {
const next = this.#queue.shift();
next();
}
}
}
async addAll(taskFns) {
return Promise.all(taskFns.map(fn => this.add(fn)));
}
}
const pool = new PromisePool(3);
const urls = Array.from({ length: 100 }, (_, i) => `/api/item/${i}`);
const results = await pool.addAll(
urls.map(url => () => fetch(url).then(r => r.json()))
);
实战案例 2:超时与取消
function withTimeout(promise, ms, message = 'Operation timed out') {
const timeout = new Promise((_, reject) => {
const id = setTimeout(() => {
reject(new Error(message));
}, ms);
promise.finally(() => clearTimeout(id));
});
return Promise.race([promise, timeout]);
}
function cancellableFetch(url, options = {}) {
const controller = new AbortController();
const promise = fetch(url, {
...options,
signal: controller.signal
});
return {
promise,
cancel: () => controller.abort()
};
}
const { promise, cancel } = cancellableFetch('/api/data');
const result = await withTimeout(promise, 5000);
实战案例 3:Promise 缓存 / 请求去重
function createRequestDeduper() {
const inflight = new Map();
return async function dedupedFetch(url, options) {
const key = `${options?.method || 'GET'}:${url}`;
if (inflight.has(key)) {
return inflight.get(key);
}
const promise = fetch(url, options)
.then(r => r.json())
.finally(() => {
inflight.delete(key);
});
inflight.set(key, promise);
return promise;
};
}
const dedupedFetch = createRequestDeduper();
const [r1, r2, r3] = await Promise.all([
dedupedFetch('/api/user/1'),
dedupedFetch('/api/user/1'),
dedupedFetch('/api/user/1'),
]);
9. 深度追问
Q1:为什么 Promise A+ 规范要求 then 回调必须异步执行?
为了保证一致的执行顺序。如果同步执行,那么 promise.then(fn) 的行为会因 promise 当前状态不同而不同——已解决时同步调用 fn,未解决时异步调用。这种不确定性会导致难以调试的竞态条件(Zalgo 问题)。
Q2:Promise.resolve 和 new Promise(resolve => resolve(x)) 有区别吗?
当 x 是 Promise 实例时有区别:Promise.resolve(x) 直接返回 x 本身(同一引用),而 new Promise(resolve => resolve(x)) 会创建一个新 Promise 并跟踪 x 的状态(多一个微任务延迟)。
Q3:为什么 async 函数总是返回 Promise?
ECMAScript 规范(§15.8.3)定义 async 函数的 [[Call]] 内部方法会创建一个 Promise,函数体的同步返回值作为 resolve 参数,throw 的异常作为 reject 参数。这确保了 async 函数的调用者总是得到一致的异步接口。
10. 总结表格
| Promise 方法 | 行为 | 短路条件 |
|---|
Promise.all | 全部成功才成功 | 一个 reject 就 reject |
Promise.allSettled | 等待全部完成 | 无短路 |
Promise.race | 采用第一个完成的 | 第一个 settle |
Promise.any | 采用第一个成功的 | 一个 resolve 就 resolve |
| 概念 | 规范引用 | 关键行为 |
|---|
| 状态不可逆 | A+ §2.1 | fulfilled/rejected 后锁定 |
| 异步回调 | A+ §2.2.4 | then 回调必须异步 |
| 值穿透 | A+ §2.2.1/7 | 非函数参数被忽略 |
| thenable 协议 | A+ §2.3.3 | 支持任意实现互操作 |
| 防重入 | A+ §2.3.3.3.3 | called 标志位 |
| 性能对比 | 原生 Promise | 手写 Promise |
|---|
| 内存 | 56 bytes (V8) | ~200 bytes |
| then 注册 | 内联 PromiseReaction | Array push |
| 微任务调度 | C++ 层直接入队 | queueMicrotask |
| GC 优化 | 有 | 无 |