自定义 Promise 实现:深入解析 then、catch 和 finally
大家好!今天我们来一起深入探讨如何实现一个自定义的 Promise
,并深入解析其 then
、catch
和 finally
的执行逻辑。Promise
作为现代 JavaScript 中处理异步操作的重要工具,理解其内部机制对于编写高效、可维护的代码至关重要。
Promise 的基本概念
在开始实现之前,我们先回顾一下 Promise
的几个关键概念:
- 状态 (State):
Promise
具有三种状态:- Pending (待定): 初始状态,既没有被兑现,也没有被拒绝。
- Fulfilled (已兑现): 操作成功完成。
- Rejected (已拒绝): 操作失败。
- 值 (Value):
Promise
对象保存着一个值,该值在Promise
状态变为Fulfilled
时可用。 - 原因 (Reason):
Promise
对象也可能保存一个原因,该原因在Promise
状态变为Rejected
时可用。 - 不可变性 (Immutability): 一旦
Promise
的状态变为Fulfilled
或Rejected
,它就不能再改变。 then
方法: 用于注册在Promise
状态变为Fulfilled
或Rejected
时要执行的回调函数。catch
方法: 用于注册在Promise
状态变为Rejected
时要执行的回调函数,是then(null, rejection)
的语法糖。finally
方法: 用于注册在Promise
状态变为Fulfilled
或Rejected
时都要执行的回调函数,无论结果如何。
实现 Promise 骨架
首先,我们创建一个 MyPromise
类,并定义其构造函数和状态。
class MyPromise {
constructor(executor) {
this.state = 'pending'; // 初始状态为 pending
this.value = undefined; // 成功的值
this.reason = undefined; // 失败的原因
this.onFulfilledCallbacks = []; // 存储成功的回调
this.onRejectedCallbacks = []; // 存储失败的回调
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
resolve(value) {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn()); // 执行所有成功的回调
}
}
reject(reason) {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn()); // 执行所有失败的回调
}
}
then(onFulfilled, onRejected) {
// ... 实现 then 方法
}
catch(onRejected) {
return this.then(null, onRejected); // catch 是 then 的语法糖
}
finally(callback) {
// ... 实现 finally 方法
}
}
在构造函数中,我们接收一个 executor
函数,该函数接收 resolve
和 reject
两个函数作为参数。executor
函数负责执行异步操作,并在操作成功时调用 resolve
,操作失败时调用 reject
。
我们还定义了 onFulfilledCallbacks
和 onRejectedCallbacks
数组,用于存储 then
方法注册的回调函数。当 Promise
的状态变为 fulfilled
或 rejected
时,我们会依次执行这些回调函数。
实现 then
方法
then
方法是 Promise
的核心方法,它用于注册在 Promise
状态变为 fulfilled
或 rejected
时要执行的回调函数。then
方法返回一个新的 Promise
对象,允许链式调用。
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2;
}
这个 then
方法的实现包含以下几个关键步骤:
- 参数处理: 首先,我们对
onFulfilled
和onRejected
进行类型检查。如果它们不是函数,则提供默认的函数:onFulfilled
默认函数返回value
,实现值的穿透。onRejected
默认函数抛出reason
,实现错误穿透。
- 创建新的 Promise: 创建一个新的
MyPromise
对象promise2
,并返回它。这是实现链式调用的关键。 - 处理不同的状态: 根据当前
Promise
的状态,执行不同的逻辑:- fulfilled: 异步执行
onFulfilled
,并将结果x
传递给resolvePromise
函数。 - rejected: 异步执行
onRejected
,并将结果x
传递给resolvePromise
函数。 - pending: 将
onFulfilled
和onRejected
函数分别添加到onFulfilledCallbacks
和onRejectedCallbacks
数组中,等待Promise
状态改变时执行。 使用setTimeout
是为了模拟异步,保证promise2
创建完成
- fulfilled: 异步执行
- 异步执行: 使用
setTimeout
模拟异步执行,确保在当前调用栈清空后执行回调函数。 resolvePromise
函数: 这个函数用于处理onFulfilled
或onRejected
回调函数的返回值x
,并根据x
的类型来决定promise2
的状态。 这个函数非常重要,处理了then返回的值x的情况,让then可以链式调用。
实现 resolvePromise
函数
resolvePromise
函数用于处理 onFulfilled
或 onRejected
回调函数的返回值 x
,并根据 x
的类型来决定 promise2
的状态。这是 Promise
实现中最复杂的部分。
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'));
}
let called = false; // 防止多次调用 resolve 或 reject
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
const then = x.then; // 取出 x 的 then 方法
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject); // 递归调用 resolvePromise
},
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
if (called) return;
called = true;
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
if (called) return;
called = true;
resolve(x);
}
}
resolvePromise
函数的逻辑如下:
- 循环引用检测: 首先,检查
promise2
是否等于x
。如果相等,说明发生了循环引用,抛出一个TypeError
。 - 判断
x
的类型: 判断x
是否为对象或函数。- 如果
x
是对象或函数:- 尝试取出
x
的then
方法。 - 如果
then
是一个函数,则认为x
是一个Promise
对象。调用then
方法,并将resolve
和reject
作为参数传递给它。 - 如果
then
不是一个函数,则将x
作为值传递给resolve
函数,使promise2
变为fulfilled
状态。
- 尝试取出
- 如果
x
不是对象或函数: 将x
作为值传递给resolve
函数,使promise2
变为fulfilled
状态。
- 如果
- 防止多次调用: 使用
called
变量来防止多次调用resolve
或reject
函数。 - 错误处理: 使用
try...catch
块来捕获可能发生的错误,并将错误传递给reject
函数,使promise2
变为rejected
状态。
实现 finally
方法
finally
方法用于注册在 Promise
状态变为 fulfilled
或 rejected
时都要执行的回调函数,无论结果如何。finally
方法返回一个新的 Promise
对象,并将原始 Promise
的值或原因传递给它。
finally(callback) {
return this.then(
value => {
return MyPromise.resolve(callback()).then(() => value);
},
reason => {
return MyPromise.resolve(callback()).then(() => { throw reason; });
}
);
}
finally
方法的实现原理如下:
- 使用
then
方法:finally
方法实际上是基于then
方法实现的。 - 无论成功或失败都执行回调: 在
then
方法的onFulfilled
和onRejected
回调函数中,都调用callback
函数。 - 返回原始值或原因: 在
callback
函数执行完毕后,返回原始Promise
的值或原因,以保持Promise
链的完整性。- 如果原始
Promise
状态为fulfilled
,则返回value
。 - 如果原始
Promise
状态为rejected
,则抛出reason
。
- 如果原始
- 确保回调异步执行完成: 使用
MyPromise.resolve(callback())
确保callback
函数异步执行完成,避免阻塞Promise
链。
完善 Promise 实现:静态方法
为了使我们的 MyPromise
更完整,我们还需要实现一些静态方法,例如 resolve
、reject
和 all
。
MyPromise.resolve(value)
:
static resolve(value) {
return new MyPromise((resolve, reject) => {
resolve(value);
});
}
MyPromise.reject(reason)
:
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
MyPromise.all(promises)
:
static all(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
const result = [];
let count = 0;
if (promises.length === 0) {
return resolve(result);
}
for (let i = 0; i < promises.length; i++) {
const promise = promises[i];
MyPromise.resolve(promise).then(
value => {
result[i] = value;
count++;
if (count === promises.length) {
resolve(result);
}
},
reason => {
reject(reason);
}
);
}
});
}
MyPromise.all(promises)
方法的逻辑如下:
- 参数校验: 首先,检查
promises
是否为数组。如果不是,则抛出一个TypeError
。 - 初始化: 创建一个空数组
result
用于存储结果,并初始化计数器count
为 0。 - 空数组处理: 如果
promises
数组为空,则直接返回一个fulfilled
状态的Promise
,值为result
。 - 遍历
promises
数组: 遍历promises
数组,并将每个元素转换为Promise
对象。 - 处理每个
Promise
对象: 对于每个Promise
对象,使用then
方法注册回调函数:- 成功回调: 将
Promise
的值添加到result
数组中,并将计数器count
加 1。如果count
等于promises
数组的长度,则说明所有Promise
对象都已成功,将result
作为值传递给resolve
函数,使Promise
变为fulfilled
状态。 - 失败回调: 将
reason
作为原因传递给reject
函数,使Promise
变为rejected
状态。
- 成功回调: 将
测试我们的 Promise 实现
现在,我们可以使用一些简单的测试用例来验证我们的 MyPromise
实现是否正确。
const promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 resolved');
}, 1000);
});
promise1
.then(value => {
console.log('then 1:', value);
return 'then 1 return';
})
.then(value => {
console.log('then 2:', value);
throw new Error('Error in then 2');
})
.catch(error => {
console.error('catch:', error);
})
.finally(() => {
console.log('finally');
});
MyPromise.resolve('Resolved value')
.then(value => console.log('Resolved promise:', value));
MyPromise.reject('Rejected reason')
.catch(reason => console.error('Rejected promise:', reason));
MyPromise.all([
MyPromise.resolve(1),
new MyPromise(resolve => setTimeout(() => resolve(2), 500)),
3,
])
.then(values => console.log('All resolved:', values))
.catch(reason => console.error('All rejected:', reason));
总结重点
我们深入探讨了如何实现一个自定义的 Promise
,并详细解析了 then
、catch
和 finally
的执行逻辑。重点在于对 Promise
状态的维护、回调函数的管理、以及 resolvePromise
函数的处理,它决定了链式调用中 Promise
的状态传递和结果处理。
进一步的思考方向
- 异步任务调度: 研究如何优化异步任务的调度,例如使用微任务队列(microtask queue)来提高性能。
- Promise A+ 规范: 深入理解 Promise A+ 规范,确保你的 Promise 实现符合标准。
- 错误处理: 探索更高级的错误处理机制,例如使用
unhandledRejection
事件来捕获未处理的 Promise 拒绝。
希望今天的分享对大家有所帮助,谢谢!