跳到主要内容

手写 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 不能是自身)

相关链接