手写 Promise
问题
如何手写代码实现一个简单的 Promise?
答案
实现一个符合 Promises/A+ 规范的 Promise,需要理解其核心概念:状态管理、回调存储、链式调用。
核心概念
Promise 三种状态
- pending:初始状态,可以转换为 fulfilled 或 rejected
- fulfilled:操作成功完成
- rejected:操作失败
状态一旦改变就不可逆转。
基础版实现
type PromiseState = 'pending' | 'fulfilled' | 'rejected';
type Resolve<T> = (value: T) => void;
type Reject = (reason: any) => void;
type Executor<T> = (resolve: Resolve<T>, reject: Reject) => void;
class MyPromise<T> {
private state: PromiseState = 'pending';
private value: T | undefined = undefined;
private reason: any = undefined;
private onFulfilledCallbacks: Array<() => void> = [];
private onRejectedCallbacks: Array<() => void> = [];
constructor(executor: Executor<T>) {
const resolve: Resolve<T> = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 执行所有成功回调
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject: Reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 执行所有失败回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
): MyPromise<TResult1 | TResult2> {
// 参数默认值处理
const realOnFulfilled = typeof onFulfilled === 'function'
? onFulfilled
: (value: T) => value as unknown as TResult1;
const realOnRejected = typeof onRejected === 'function'
? onRejected
: (reason: any) => { throw reason; };
// 返回新的 Promise 实现链式调用
const promise2 = new MyPromise<TResult1 | TResult2>((resolve, reject) => {
const handleFulfilled = () => {
// 使用微任务确保异步执行
queueMicrotask(() => {
try {
const x = realOnFulfilled(this.value as T);
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
const handleRejected = () => {
queueMicrotask(() => {
try {
const x = realOnRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
if (this.state === 'fulfilled') {
handleFulfilled();
} else if (this.state === 'rejected') {
handleRejected();
} else {
// pending 状态,存储回调
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
return promise2;
}
// 处理 then 返回值
private resolvePromise<R>(
promise2: MyPromise<R>,
x: any,
resolve: Resolve<R>,
reject: Reject
): void {
// 防止循环引用
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected'));
}
// 如果 x 是 Promise,等待其完成
if (x instanceof MyPromise) {
x.then(resolve, reject);
return;
}
// 如果 x 是 thenable 对象
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: any) => {
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
},
(r: any) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
resolve(x);
}
}
catch<TResult = never>(
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
): MyPromise<T | TResult> {
return this.then(null, onRejected);
}
finally(onFinally?: (() => void) | null): MyPromise<T> {
return this.then(
value => {
onFinally?.();
return value;
},
reason => {
onFinally?.();
throw reason;
}
);
}
}
静态方法实现
class MyPromise<T> {
// ... 上面的代码 ...
static resolve<U>(value: U | PromiseLike<U>): MyPromise<U> {
if (value instanceof MyPromise) {
return value;
}
return new MyPromise<U>(resolve => resolve(value as U));
}
static reject<U = never>(reason: any): MyPromise<U> {
return new MyPromise<U>((_, reject) => reject(reason));
}
static all<T extends readonly unknown[]>(
promises: T
): MyPromise<{ -readonly [P in keyof T]: Awaited<T[P]> }> {
return new MyPromise((resolve, reject) => {
const results: any[] = [];
let completedCount = 0;
const promiseArray = Array.from(promises);
if (promiseArray.length === 0) {
resolve([] as any);
return;
}
promiseArray.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = value;
completedCount++;
if (completedCount === promiseArray.length) {
resolve(results as any);
}
},
reason => {
reject(reason);
}
);
});
});
}
static race<T extends readonly unknown[]>(
promises: T
): MyPromise<Awaited<T[number]>> {
return new MyPromise((resolve, reject) => {
const promiseArray = Array.from(promises);
promiseArray.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
}
static allSettled<T extends readonly unknown[]>(
promises: T
): MyPromise<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>> }> {
return new MyPromise(resolve => {
const results: PromiseSettledResult<any>[] = [];
let completedCount = 0;
const promiseArray = Array.from(promises);
if (promiseArray.length === 0) {
resolve([] as any);
return;
}
promiseArray.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = { status: 'fulfilled', value };
completedCount++;
if (completedCount === promiseArray.length) {
resolve(results as any);
}
},
reason => {
results[index] = { status: 'rejected', reason };
completedCount++;
if (completedCount === promiseArray.length) {
resolve(results as any);
}
}
);
});
});
}
static any<T extends readonly unknown[]>(
promises: T
): MyPromise<Awaited<T[number]>> {
return new MyPromise((resolve, reject) => {
const errors: any[] = [];
let rejectedCount = 0;
const promiseArray = Array.from(promises);
if (promiseArray.length === 0) {
reject(new AggregateError([], 'All promises were rejected'));
return;
}
promiseArray.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
resolve(value);
},
reason => {
errors[index] = reason;
rejectedCount++;
if (rejectedCount === promiseArray.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
});
});
}
}
使用示例
// 基本使用
const promise = new MyPromise<string>((resolve, reject) => {
setTimeout(() => {
resolve('成功');
}, 1000);
});
promise
.then(value => {
console.log(value); // '成功'
return value + '!';
})
.then(value => {
console.log(value); // '成功!'
})
.catch(error => {
console.error(error);
});
// Promise.all
MyPromise.all([
MyPromise.resolve(1),
MyPromise.resolve(2),
MyPromise.resolve(3)
]).then(results => {
console.log(results); // [1, 2, 3]
});
// Promise.race
MyPromise.race([
new MyPromise(resolve => setTimeout(() => resolve('慢'), 200)),
new MyPromise(resolve => setTimeout(() => resolve('快'), 100))
]).then(result => {
console.log(result); // '快'
});
关键点总结
| 要点 | 说明 |
|---|---|
| 状态不可逆 | pending 只能变为 fulfilled 或 rejected,且只能变化一次 |
| 异步执行 | then 的回调必须异步执行(微任务) |
| 链式调用 | then 返回新的 Promise |
| 值穿透 | then 参数不是函数时,值会穿透到下一个 then |
| 错误冒泡 | 错误会沿着链传递直到被 catch 捕获 |
注意事项
queueMicrotask用于模拟原生 Promise 的微任务行为resolvePromise方法处理了各种返回值情况,包括 thenable 对象- 需要防止循环引用(then 返回的 Promise 不能是自身)