前言
在现代 JavaScript 开发中,Promise 已成为处理异步操作的基石。它优雅地解决了回调地狱问题,让异步代码拥有了同步代码般的可读性和可维护性。但你是否真正理解 Promise 的内部工作机制?本文将带你从零开始,逐步构建一个符合 Promises/A+ 规范的 Promise 实现,深入剖析其核心机制。
一、Promise 的核心概念
1.1 状态机:Promise 的三大状态
Promise 本质上是一个状态机,它有三种状态:
- pending:初始状态,既不是成功也不是失败
- fulfilled:操作成功完成
- rejected:操作失败
状态转换是不可逆的,只能从 pending 变为 fulfilled 或从 pending 变为 rejected。这种设计保证了 Promise 行为的确定性。
1.2 Thenable 接口:链式调用的基础
Promise 的核心是 then 方法,它注册了当 Promise 完成或拒绝时的回调函数。then 方法返回一个新的 Promise,这使得链式调用成为可能。
二、基础架构设计
让我们从构造函数开始,逐步构建我们的 Promise 实现。
javascript
class MyPromise {
constructor(executor) {
// 初始状态
this.status = 'pending';
// 成功时传递的值
this.value = undefined;
// 失败时传递的原因
this.reason = undefined;
// 存储成功回调队列
this.onFulfilledCallbacks = [];
// 存储失败回调队列
this.onRejectedCallbacks = [];
// 定义resolve函数
const resolve = (value) => {
// 只有pending状态可以转换
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
// 执行所有成功回调
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
// 定义reject函数
const reject = (reason) => {
// 只有pending状态可以转换
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
// 执行所有失败回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 立即执行executor函数
try {
executor(resolve, reject);
} catch (error) {
// 如果executor执行抛出异常,直接reject
reject(error);
}
}
}
三、 Then 方法的深度解析
then 方法是 Promise 最复杂也最核心的部分,它需要处理多种情况。
javascript
then(onFulfilled, onRejected) {
// 处理参数不是函数的情况 - 值穿透
onFulfilled = typeof onFulfilled === 'function'
? onFulfilled
: value => value;
onRejected = typeof onRejected === 'function'
? onRejected
: reason => { throw reason };
// 返回新的Promise,实现链式调用
const newPromise = new MyPromise((resolve, reject) => {
// 处理已完成状态
if (this.status === 'fulfilled') {
// 使用setTimeout确保异步执行
setTimeout(() => {
try {
// 执行成功回调
const result = onFulfilled(this.value);
// 解析返回值
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
// 捕获回调执行中的错误
reject(error);
}
}, 0);
}
// 处理已拒绝状态
if (this.status === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
// 处理pending状态
if (this.status === 'pending') {
// 将回调函数存入队列
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return newPromise;
}
1. 值穿透机制
当 then 方法的参数不是函数时,我们需要提供默认实现确保值能正确传递到链中的下一个 Promise。
javascript
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
这保证了以下代码能正常工作。
javascript
promise.then().then(value => console.log(value));
2. 异步执行保证
Promises/A+ 规范明确要求 then 方法的回调必须异步执行。我们使用 setTimeout
来模拟微任务。
javascript
setTimeout(() => {
// 回调逻辑
}, 0);
虽然原生 Promise 使用微任务队列而非宏任务,但 setTimeout
帮助我们满足了"异步执行"的基本要求。
3. 错误处理
使用 try...catch
包装回调执行过程,确保任何异常都能被捕获并传递给下一个 Promise。
javascript
try {
const result = onFulfilled(this.value);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
四、Promise 解析过程:resolvePromise 方法
resolvePromise 方法是实现符合 Promises/A+ 规范的关键,它处理了 then 方法返回值的多种情况。
javascript
resolvePromise(promise, x, resolve, reject) {
// 防止循环引用
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 防止多次调用
let called = false;
// 如果x是对象或函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
// 获取x的then方法
const then = x.then;
// 如果then是函数,假定x为Promise或thenable对象
if (typeof then === 'function') {
then.call(
x,
// resolve回调
y => {
if (called) return;
called = true;
// 递归解析,直到返回值不是Promise
this.resolvePromise(promise, y, resolve, reject);
},
// reject回调
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// 如果x是普通对象或函数,但没有then方法
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
// 如果x是基本类型值
resolve(x);
}
}
1. 循环引用检测
防止 Promise 与自身循环引用。
javascript
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
2. Thenable 对象处理
处理具有 then 方法的对象(thenable),这是 Promise interoperability 的基础。
javascript
if (typeof then === 'function') {
then.call(
x,
y => {
// 递归解析
this.resolvePromise(promise, y, resolve, reject);
},
r => {
reject(r);
}
);
}
3. 确保只执行一次
使用 called 标志位确保 resolve 或 reject 只执行一次。
javascript
let called = false;
// 在回调中检查
if (called) return;
called = true;
五、 Catch 方法与错误处理
catch 方法是 then 方法的语法糖,专门用于错误处理。
javascript
catch(onRejected) {
return this.then(null, onRejected);
}
这种设计保持了 API 的简洁性,同时充分利用了 then 方法已有的功能。
六、完整实现代码
将以上各部分组合起来,我们得到完整的 Promise 实现。
javascript
class MyPromise {
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
const newPromise = new MyPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return newPromise;
}
catch(onRejected) {
return this.then(null, onRejected);
}
resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called = false;
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return;
called = true;
this.resolvePromise(promise, y, resolve, reject);
},
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
resolve(x);
}
}
}
总结
虽然我们的实现基本符合 Promises/A+ 规范,但与原生 Promise 仍存在一些重要差异,如原生 Promise 使用微任务队列,而我们使用 setTimeout
(宏任务),我们未实现 Promise.all、Promise.race、Promise.finally
等静态方法。但通过手动实现 Promise,我们深入理解其内部工作机制:
- 状态管理:Promise 是一个状态机,具有明确的状态转换规则
- 回调队列:使用数组存储回调函数,支持多个 then 调用
- 链式调用:then 方法返回新 Promise 是实现链式调用的基础
- 值穿透:处理非函数参数确保值正确传递
- 异步保证:确保回调总是异步执行,符合 Promises/A+ 规范
- Promise 解析:递归解析 thenable 对象,实现 interoperability
理解 Promise 的内部实现不仅有助于我们更好地使用它,也能帮助我们在面对复杂异步场景时做出更合理的设计决策。希望本文能帮助你深入理解 Promise 的工作原理,如果有任何疑问或建议,欢迎在评论区讨论!