Promise A+
引言
Promise 对象表示异步操作的最终完成(或失败)及其结果值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来,使异步方法能够像同步方法一样返回值——异步方法不是立即返回最终值,而是返回一个 promise,承诺在未来某个时刻提供该值。
Promise/A+ 是一个开放的、健全的、可互操作的 JavaScript Promise 规范——由实现者为实现者制定。该规范详细说明了 then 方法的行为,提供了一个可互操作的基础,所有符合 Promise/A+ 的 Promise 实现都可以依赖这个基础。
核心术语
在深入规范之前,我们需要理解以下关键术语:
| 术语 | 定义 |
| promise | 一个具有 then 方法的对象或函数,其行为符合本规范 |
| thenable | 一个定义了 then 方法的对象或函数 |
| value(值) | 任何合法的 JavaScript 值(包括 undefined、thenable 或 promise) |
| exception(异常) | 使用 throw 语句抛出的值 |
| reason(原因) | 表示 promise 被拒绝原因的值 |
Promise 状态
Promise 必须处于以下三种状态之一:pending(等待)、fulfilled(已完成) 或 rejected(已拒绝)。
状态转换规则
┌─────────────────────────────────────────────────────────────┐
│ pending │
│ (初始状态) │
└──────────────────┬─────────────────────┬────────────────────┘
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────────┐
│ fulfilled │ │ rejected │
│ (操作成功完成) │ │ (操作失败) │
│ 必须有一个不可变的值 │ │ 必须有一个不可变的原因 │
└──────────────────────────┘ └──────────────────────────────┘===,而非深度不可变性)。then 方法规范
Promise 必须提供 then 方法来访问其当前或最终的值或原因。
promise.then(onFulfilled, onRejected)参数要求
onFulfilled和onRejected都是可选参数- 如果
onFulfilled不是函数,必须忽略它 - 如果
onRejected不是函数,必须忽略它
- 如果
- 如果
onFulfilled是函数- 必须在 promise 完成后调用,以 promise 的值作为第一个参数
- 不能在 promise 完成前调用
- 不能被调用多次
- 如果
onRejected是函数- 必须在 promise 被拒绝后调用,以 promise 的原因作为第一个参数
- 不能在 promise 被拒绝前调用
- 不能被调用多次
- 异步执行保证
onFulfilled或onRejected必须在执行上下文栈只包含平台代码时才能调用
Promise 解析过程
Promise 解析过程是一个抽象操作,接受一个 promise 和一个值作为输入,记为 [[Resolve]](promise, x)。
解析步骤
步骤 1:检测循环引用
如果 promise 和 x 引用同一对象,以 TypeError 为原因拒绝 promise。
步骤 2:如果 x 是 Promise
采用 x 的状态:
- 如果
x处于 pending,promise 必须保持 pending 直到x完成或被拒绝 - 如果
x已完成,以相同的值完成 promise - 如果
x已拒绝,以相同的原因拒绝 promise
步骤 3:如果 x 是对象或函数
- 获取
then = x.then(如果抛出异常,以该异常拒绝 promise) - 如果
then是函数,以x为this调用它,传入resolvePromise和rejectPromise:- 如果
resolvePromise被调用并传入值y,运行[[Resolve]](promise, y) - 如果
rejectPromise被调用并传入原因r,以r拒绝 promise - 如果两者都被调用,或同一参数被多次调用,第一次调用优先,后续调用被忽略
- 如果调用
then抛出异常,且resolvePromise
- 如果
步骤 4:如果 x 不是对象或函数
以 x 完成 promise。
完整实现
以下是一个完整的、符合 Promise/A+ 规范并通过全部测试的 TypeScript 实现:
类型定义
interface PromiseLike<T> {
then<TResult1 = T, TResult2 = never>(
onfulfilled?:
| ((value: T) => TResult1 | PromiseLike<TResult1>)
| undefined
| null,
onrejected?:
| ((reason: any) => TResult2 | PromiseLike<TResult2>)
| undefined
| null
): PromiseLike<TResult1 | TResult2>;
}内部状态
使用 Symbol 来存储 Promise 的内部状态,确保外部无法直接访问:
const promiseState = Symbol("PromiseState");
const promiseResult = Symbol("PromiseResult");Promise 解析函数
这是实现中最核心的部分,完整实现了 [[Resolve]](promise, x) 算法:
function resolvePromise<T>(
x: unknown,
promise: Promise<T>,
resolve: (value: T | PromiseLike<T>) => void,
reject: (reason: any) => void
) {
// 步骤 1:检测循环引用
if (x === promise) {
reject(new TypeError(`Circular reference between ${x} and ${promise}`));
return;
}
// 步骤 2:如果 x 是 Promise,采用其状态
if (x instanceof Promise) {
x.then((value) => {
resolvePromise(value, promise, resolve, reject);
}, reject);
return;
}
// 步骤 3:如果 x 是对象或函数
if (
(typeof x === "function" || (typeof x === "object" && x !== null)) &&
"then" in x
) {
let called = false; // 确保只调用一次
try {
const then = x.then;
if (typeof then === "function") {
then.call(
x,
(value: unknown) => {
if (called) return;
called = true;
resolvePromise(value, promise, resolve, reject);
},
(reason: unknown) => {
if (called) return;
called = true;
reject(reason);
}
);
return;
}
resolve(x as any);
} catch (e) {
if (called) return;
reject(e);
}
return;
}
// 步骤 4:直接完成
resolve(x as any);
}Promise 类
export class Promise<T = unknown> {
[promiseState]: "pending" | "fulfilled" | "rejected" = "pending";
[promiseResult]: T | undefined;
#onfulfilleds: ((value: any) => any)[] = [];
#onrejecteds: ((reason: any) => any)[] = [];
constructor(
executor: (
resolve: (value: T | PromiseLike<T>) => void,
reject: (reason?: any) => void
) => void
) {
const resolve: (value: T | PromiseLike<T>) => void = (value) => {
if (this[promiseState] !== "pending") return;
resolvePromise(
value,
this,
(value) => {
this[promiseState] = "fulfilled";
this[promiseResult] = value as T;
this.#onfulfilleds.forEach((cb) => {
cb(value);
});
},
reject
);
};
const reject: (reason?: any) => void = (reason) => {
if (this[promiseState] !== "pending") return;
this[promiseState] = "rejected";
this[promiseResult] = reason;
this.#onrejecteds.forEach((cb) => {
cb(reason);
});
};
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then<TResult1 = T, TResult2 = never>(
onfulfilled?:
| ((value: T) => TResult1 | PromiseLike<TResult1>)
| undefined
| null,
onrejected?:
| ((reason: any) => TResult2 | PromiseLike<TResult2>)
| undefined
| null
): Promise<TResult1 | TResult2> {
// 处理非函数参数:值穿透
onfulfilled =
typeof onfulfilled === "function"
? onfulfilled
: (value) => value as unknown as TResult1;
onrejected =
typeof onrejected === "function"
? onrejected
: (reason) => {
throw reason;
};
switch (this[promiseState]) {
case "pending": {
// pending 状态:将回调存入队列
const promise = new Promise<TResult1 | TResult2>((resolve, reject) => {
this.#onfulfilleds.push((value) => {
queueMicrotask(() => {
try {
const result = onfulfilled(value);
resolvePromise(result, promise, resolve, reject);
} catch (e) {
reject(e);
}
});
});
this.#onrejecteds.push((reason) => {
queueMicrotask(() => {
try {
const result = onrejected(reason);
resolvePromise(result, promise, resolve, reject);
} catch (e) {
reject(e);
}
});
});
});
return promise;
}
case "fulfilled": {
// fulfilled 状态:异步执行 onFulfilled
const promise = new Promise<TResult1 | TResult2>((resolve, reject) => {
queueMicrotask(() => {
try {
const result = onfulfilled(this[promiseResult] as T);
resolvePromise(result, promise, resolve, reject);
} catch (e) {
reject(e);
}
});
});
return promise;
}
case "rejected": {
// rejected 状态:异步执行 onRejected
const promise = new Promise<TResult1 | TResult2>((resolve, reject) => {
queueMicrotask(() => {
try {
const result = onrejected(this[promiseResult] as any);
resolvePromise(result, promise, resolve, reject);
} catch (e) {
reject(e);
}
});
});
return promise;
}
}
}
}静态方法
// Promise.resolve
static resolve(): Promise<void>;
static resolve<T>(value: T): Promise<Awaited<T>>;
static resolve<T>(value: T | PromiseLike<T>): Promise<Awaited<T>>;
static resolve(value?: any): any {
if (value instanceof Promise) return value;
return new Promise((resolve) => {
resolve(value);
});
}
// Promise.reject
static reject<T = never>(reason?: any): Promise<T> {
return new Promise((resolve, reject) => {
reject(reason);
});
}测试适配器
Promise/A+ 测试套件需要一个 deferred 函数:
function deferred<T>() {
const result = {} as {
promise: Promise<T>;
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason: any) => void;
};
const promise = new Promise<T>((resolve, reject) => {
result.reject = reject;
result.resolve = resolve;
});
result.promise = promise;
return result;
}
export { resolve, reject, deferred };关键实现细节
1. 异步执行保证
使用 queueMicrotask 确保回调异步执行:
queueMicrotask(() => {
try {
const result = onfulfilled(value);
resolvePromise(result, promise, resolve, reject);
} catch (e) {
reject(e);
}
});onFulfilled 和 onRejected 必须在执行上下文栈只包含"平台代码"时才能调用。queueMicrotask 是现代浏览器推荐的微任务调度方式,也可以使用 setTimeout(宏任务)或 MutationObserver(微任务)实现。2. 值穿透
当 onFulfilled 或 onRejected 不是函数时,实现值穿透:
onfulfilled =
typeof onfulfilled === "function"
? onfulfilled
: (value) => value as unknown as TResult1;
onrejected =
typeof onrejected === "function"
? onrejected
: (reason) => {
throw reason;
};这确保了 .then(null, null) 或 .then() 能正确传递值或原因。
3. 防止重复调用
在处理 thenable 时,使用 called 标志防止 resolvePromise 和 rejectPromise 被多次调用:
let called = false;
// ...
if (called) return;
called = true;测试验证
使用官方的 promises-aplus-tests 测试套件验证实现:
npm install promises-aplus-tests -D// test.js
const adapter = require('./promise');
const promisesAplusTests = require('promises-aplus-tests');
promisesAplusTests(adapter, (err) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log('All tests passed!');
});与 ECMAScript Promise 的关系
ES2015 将 Promise 纳入语言标准,其规范基于 Promise/A+ 但有所扩展:
| 特性 | Promise/A+ | ECMAScript Promise |
| 核心规范 | 仅定义 then 方法 | 完整的 Promise API |
| 静态方法 | 未定义 | all、race、allSettled、any、resolve、reject、withResolvers、try |
| 实例方法 | 仅 then | then、catch、finally |
Promise 全局对象是 Promise/A+ 的一个实现,但 Promise/A+ 实现不必完全符合 ECMAScript 规范的所有要求。