手写一个符合 Promise/A+ 规范的 Promise(附完整代码)
本文将带你一步步实现一个可用的 Promise,包含状态管理、链式调用、异步微任务、错误穿透等核心特性。
一、基础结构:状态与 resolve/reject
Promise 的核心是状态机。我们定义三种状态:pending、fulfilled、rejected。初始为 pending,一旦改变就不能再次变化。
javascript
class MyPromise {
constructor(fn) {
this.state = 'pending'; // 当前状态
this.value = null; // 存储结果
fn(this.resolve, this.reject);
}
resolve = (value) => {
if (this.state !== 'pending') return; // 状态已定,不再执行
this.value = value;
this.state = 'fulfilled';
}
reject = (value) => {
if (this.state !== 'pending') return;
this.value = value;
this.state = 'rejected';
}
}
注意 :resolve 和 reject 使用箭头函数定义,确保 this 指向实例,避免作为回调传递时丢失。
二、实现 then 的基本要求
then 方法必须返回一个新的 Promise,并且其回调要在微任务中执行(异步)。
javascript
then(onFulfilled) {
const nextPromise = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
queueMicrotask(() => {
const res = onFulfilled(this.value);
resolve(res);
});
}
});
return nextPromise;
}
此时仅处理了 fulfilled 状态,且没有存储回调。
三、支持异步:回调队列
如果 then 在 resolve 之前调用,需要把回调存起来,等 resolve 时再执行。
javascript
constructor(fn) {
// ...
this.fulfilledCallback = []; // 存储 then 的成功回调
this.rejectedCallback = []; // 存储 then 的失败回调
}
then(onFulfilled) {
const nextPromise = new MyPromise((resolve, reject) => {
if (this.state === 'pending') {
this.fulfilledCallback.push(() => {
const res = onFulfilled(this.value);
resolve(res);
});
} else if (this.state === 'fulfilled') {
queueMicrotask(() => {
const res = onFulfilled(this.value);
resolve(res);
});
}
});
return nextPromise;
}
resolve 中需要遍历并执行队列里的回调:
javascript
resolve = (value) => {
if (this.state !== 'pending') return;
this.value = value;
this.state = 'fulfilled';
const callbacks = this.fulfilledCallback.slice(); // 复制后清空
this.fulfilledCallback = [];
for (let cb of callbacks) {
queueMicrotask(() => cb(value));
}
}
四、完善 then 方法:处理 onRejected 和状态分支
then 应接收两个参数 onFulfilled 和 onRejected,根据当前状态决定是立即放入微任务,还是存入队列。
javascript
then(onFulfilled, onRejected) {
// 默认值处理(非函数时透传/抛错)
if (typeof onFulfilled !== 'function') onFulfilled = value => value;
if (typeof onRejected !== 'function') onRejected = error => { throw error };
const nextPromise = new MyPromise((resolve, reject) => {
if (this.state === 'pending') {
this.fulfilledCallback.push(() => {
this.executorTryAndCatch(onFulfilled, resolve, reject, this.value, nextPromise);
});
this.rejectedCallback.push(() => {
this.executorTryAndCatch(onRejected, resolve, reject, this.value, nextPromise);
});
} else if (this.state === 'fulfilled') {
queueMicrotask(() => {
this.executorTryAndCatch(onFulfilled, resolve, reject, this.value, nextPromise);
});
} else {
queueMicrotask(() => {
this.executorTryAndCatch(onRejected, resolve, reject, this.value, nextPromise);
});
}
});
return nextPromise;
}
五、提取公共逻辑 executorTryAndCatch
上面代码中反复出现的 try-catch + 返回值解析可以提取成一个方法:
javascript
executorTryAndCatch(onFunction, resolve, reject, value, nextPromise) {
try {
const res = onFunction(value);
// 循环引用检测
if (res === nextPromise) throw new TypeError('Chaining cycle error');
// 如果返回的是 Promise,则等待其完成
if (res instanceof MyPromise) res.then(resolve, reject);
else resolve(res);
} catch (err) {
reject(err);
}
}
这个方法负责:
- 执行用户回调
- 捕获异常并 reject
- 处理返回值(普通值直接 resolve,Promise 则等待)
六、实现 catch 方法
catch 就是 then(null, onRejected) 的语法糖:
javascript
catch(onRejected) {
return this.then(null, onRejected);
}
七、最终完整代码与测试
所有特性整合后,我们的 MyPromise 代码如下:
javascript
class MyPromise {
constructor(fn) {
this.state = 'pending';
this.value = null;
this.fulfilledCallback = [];
this.rejectedCallback = [];
fn(this.resolve, this.reject);
}
resolve = (value) => {
if (this.state !== 'pending') return;
this.value = value;
this.state = 'fulfilled';
const callbacks = this.fulfilledCallback.slice();
this.fulfilledCallback = [];
for (let cb of callbacks) queueMicrotask(() => cb(value));
}
reject = (value) => {
if (this.state !== 'pending') return;
this.value = value;
this.state = 'rejected';
const callbacks = this.rejectedCallback.slice();
this.rejectedCallback = [];
for (let cb of callbacks) queueMicrotask(() => cb(value));
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') onFulfilled = value => value;
if (typeof onRejected !== 'function') onRejected = error => { throw error };
const nextPromise = new MyPromise((resolve, reject) => {
if (this.state === 'pending') {
this.fulfilledCallback.push(() => {
this.executorTryAndCatch(onFulfilled, resolve, reject, this.value, nextPromise);
});
this.rejectedCallback.push(() => {
this.executorTryAndCatch(onRejected, resolve, reject, this.value, nextPromise);
});
} else if (this.state === 'fulfilled') {
queueMicrotask(() => {
this.executorTryAndCatch(onFulfilled, resolve, reject, this.value, nextPromise);
});
} else {
queueMicrotask(() => {
this.executorTryAndCatch(onRejected, resolve, reject, this.value, nextPromise);
});
}
});
return nextPromise;
}
catch(onRejected) {
return this.then(null, onRejected);
}
executorTryAndCatch(onFunction, resolve, reject, value, nextPromise) {
try {
const res = onFunction(value);
if (res === nextPromise) throw new TypeError('Chaining cycle error');
if (res instanceof MyPromise) res.then(resolve, reject);
else resolve(res);
} catch (err) {
reject(err);
}
}
}
测试代码:
javascript
const a = new MyPromise((resolve, reject) => {
reject(1);
});
const t = a.catch(res => {
res++;
console.log(res + "---------1");
return res;
});
t.then(res => {
res++;
console.log(res + "---------2");
return res;
})
.then(res => {
res = res ** 2;
console.log(res + "---------3");
return res;
})
.then(res => {
res = res * 2;
console.log(res + "---------4");
return res;
});
console.log(1);
输出:
lua
1
2---------1
3---------2
9---------3
18---------4
八、总结
我们一步步实现了一个符合 Promise 核心规范的类,关键点总结如下:
- 状态管理 :
pending→fulfilled/rejected,不可逆。 - 回调队列 :解决异步
then先于resolve的问题。 - 微任务 :使用
queueMicrotask保证回调异步执行。 - 链式调用 :
then返回新 Promise,并递归解析返回值。 - 错误处理 :
try/catch捕获异常并reject。 - 错误穿透 :默认的
onRejected会继续抛出错误。 - 循环引用检测 :避免
then返回自己的nextPromise导致死循环。
这个手写实现虽然未涵盖 Promise.all、race、finally 等静态方法,但已经揭示了 Promise 的核心原理。掌握它,你对异步编程的理解会更上一层楼。