本文将从零开始,一步步实现一个符合 Promise/A+ 规范的 Promise。
NOTE本文假设你已经了解 Promise 的基本用法。如果你还不熟悉 Promise,建议先阅读 MDN Promise 指南。
目录
Promise 基础特性
在开始手写实现之前,让我们先通过一些典型的使用案例来理解 Promise 的核心特性。这些案例涵盖了 Promise 的主要使用场景,将帮助我们更好地理解实现细节。
1. 基本构造与使用
最基本的 Promise 使用方式是通过构造函数创建实例,并通过 then/catch 处理结果:
// 创建一个异步操作的 Promise
const promise = new Promise<string>((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
const random = Math.random();
if (random > 0.5) {
resolve(`成功: ${random}`); // 异步操作成功
} else {
reject(`失败: ${random}`); // 异步操作失败
}
}, 1000);
});
// 处理 Promise 的结果
promise
.then(result => {
console.log(result); // 输出: 成功: 0.7xxxx
})
.catch(error => {
console.log(error); // 输出: 失败: 0.3xxxx
});WARNINGPromise 构造函数的参数是一个执行器函数(executor),它接收两个参数:resolve 和 reject。 executor 函数在 Promise 构造函数执行时会立即执行。
2. 链式调用
Promise 的一个重要特性是支持链式调用,每个 then 方法都返回一个新的 Promise:
const chainPromise = new Promise<number>((resolve) => {
resolve(1);
});
chainPromise
.then(value => {
console.log(value); // 1
return value + 1; // 返回值会被包装成新的 Promise
})
.then(value => {
console.log(value); // 2
return value + 1; // 继续传递给下一个 then
})
.then(value => {
console.log(value); // 3
});TIPthen 方法的返回值会被自动包装成 Promise。这使得我们可以方便地进行链式调用,处理异步操作的连续步骤。
3. 错误处理机制
Promise 提供了两种错误处理方式,使用 catch 或在 then 中处理:
const errorPromise = new Promise<never>((resolve, reject) => {
throw new Error('发生错误!'); // 直接抛出错误
});
// 方式一:使用 catch 捕获错误
errorPromise
.then(value => {
console.log('这里不会执行');
})
.catch(error => {
console.log('捕获到错误:', error.message);
});
// 方式二:在 then 的第二个参数中处理错误
errorPromise.then(
value => {
console.log('这里不会执行');
},
error => {
console.log('在 then 中捕获错误:', error.message);
}
);WARNING在 Promise 链中,如果没有正确处理错误,错误会沿着 Promise 链向下传播,直到遇到错误处理器。 建议在 Promise 链的末尾始终添加 catch 处理器。
4. 状态的单向性
Promise 的状态一旦改变就不可逆转,这保证了异步操作的可靠性:
const statePromise = new Promise<string>((resolve, reject) => {
resolve('成功'); // 状态变更为 fulfilled
reject('失败'); // 这次调用将被忽略
});
statePromise
.then(value => {
console.log(value); // 输出: 成功
})
.catch(error => {
console.log(error); // 永远不会执行
});Promise 的状态只能从 pending 变为 fulfilled 或 rejected。 一旦状态改变,就不能再变。这就是 Promise 名字的由来 - 它是一个”承诺”。
实现 MyPromise
理解了 Promise 的核心特性,我们可以开始实现自己的 Promise 了。让我们从最基础的功能开始,逐步构建一个符合 Promise/A+ 规范的实现。
1. 基础版本
首先实现 Promise 的基本功能:构造器和 then 方法。为了简化实现,我们先只考虑成功的情况(fulfilled 状态):
class MyPromise<T> {
#status: 'pending' | 'fulfilled' = 'pending'
#value?: T
#tasks: Array<() => void> = []
#resolve = (value: T) => {
this.#value = value
this.#status = 'fulfilled'
this.#tasks.forEach(task => task())
}
constructor(executor: (resolve: (value: T) => void) => void) {
executor(this.#resolve)
}
then(onFulfilled: (value: T) => void) {
if (this.#status === 'fulfilled') {
onFulfilled(this.#value!)
} else {
this.#tasks.push(() => {
queueMicrotask(() => onFulfilled(this.#value!))
})
}
}
}这个基础实现包含了 Promise 的核心机制:
- 状态管理:使用 #status 追踪 Promise 的状态
- 值存储:使用 #value 存储 Promise 的结果
- 任务队列:使用 #tasks 存储待执行的回调函数
2. 支持链式调用
为了支持链式调用,我们需要修改 then 方法,使其返回一个新的 Promise:
class MyPromise<T> {
#status: 'pending' | 'fulfilled' = 'pending'
#value?: T
#tasks: Array<() => void> = []
#resolve = (value: T) => {
this.#value = value
this.#status = 'fulfilled'
this.#tasks.forEach(task => task())
}
constructor(executor: (resolve: (value: T) => void) => void) {
executor(this.#resolve)
}
then<R>(onFulfilled: (value: T) => R) {
return new MyPromise<R>((resolve) => {
if (this.#status === 'fulfilled') {
const newValue = onFulfilled(this.#value!)
resolve(newValue)
} else {
this.#tasks.push(() => {
queueMicrotask(() => {
const newValue = onFulfilled(this.#value!)
resolve(newValue)
})
})
}
})
}
}这个改进版本的关键点是:
- then 方法现在返回新的 Promise
- 使用泛型 R 表示 then 回调的返回值类型
- 将回调的返回值传递给新 Promise 的 resolve
NOTE接下来的步骤将包括:
- 添加 rejected 状态的支持
- 实现错误处理机制
- 添加 catch 和 finally 方法
- 实现静态方法(如 Promise.all, Promise.race 等)