逐步实现一个完整的、能通过所有Test case的Promise类
2023-09-24 19:03:55
理解 Promise 的奥秘:构建一个完善的 Promise 类
简介
在异步编程的世界中,Promise 是一种强大的工具,可以帮助我们轻松处理异步操作,从而让代码更加清晰和可控。本文将深入探讨 Promise 的内部结构,并逐步实现一个完整且通过所有测试用例的 Promise 类。
Promise 的基本原理
Promise 的本质是将异步操作的结果封装在一个对象中,并提供一系列方法来处理该对象。它有三种状态:
- Pending(等待): Promise 处于初始状态,尚未完成或拒绝。
- Fulfilled(完成): Promise 已成功完成,结果可以通过
.then()
方法获取。 - Rejected(拒绝): Promise 已被拒绝,原因可以通过
.catch()
方法获取。
构建 Promise 类
1. 构造函数
Promise 类的构造函数接受一个参数,即执行器(executor)。executor 是一个函数,它将两个函数作为参数:resolve 和 reject。这两个函数用于改变 Promise 的状态并通知相关回调函数。
class Promise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// 立即执行执行器,传入 resolve 和 reject 函数
executor(resolve, reject);
}
}
2. resolve 和 reject 方法
resolve 和 reject 方法用于改变 Promise 的状态并通知相关回调函数。resolve 方法将 Promise 的状态修改为 Fulfilled,并将结果作为参数传递给 .then()
方法。reject 方法将 Promise 的状态修改为 Rejected,并将原因作为参数传递给 .catch()
方法。
resolve(value) {
if (this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = value;
// 依次执行所有已注册的 onFulfilled 回调函数
this.onFulfilledCallbacks.forEach(callback => callback(value));
}
reject(reason) {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.reason = reason;
// 依次执行所有已注册的 onRejected 回调函数
this.onRejectedCallbacks.forEach(callback => callback(reason));
}
3. then 方法
then 方法用于注册回调函数,以便在 Promise 完成或拒绝时执行。then 方法接受两个参数:onFulfilled 和 onRejected。onFulfilled 是一个函数,将在 Promise Fulfilled 时执行,并接收 Promise 的结果作为参数。onRejected 是一个函数,将在 Promise Rejected 时执行,并接收 Promise 的原因作为参数。
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
// 将回调函数添加到 onFulfilledCallbacks 或 onRejectedCallbacks 数组中
this.onFulfilledCallbacks.push(() => {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
});
this.onRejectedCallbacks.push(() => {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
});
});
}
4. catch 方法
catch 方法是 then 方法的简化版,它只接受一个参数:onRejected。catch 方法将在 Promise Rejected 时执行,并将 Promise 的原因作为参数传递给 onRejected 函数。
catch(onRejected) {
return this.then(undefined, onRejected);
}
5. 测试用例
为了确保我们的 Promise 类能够正常工作,我们需要编写一系列测试用例来对其进行验证。我们可以使用 Jest 或 Mocha 等测试框架来编写测试用例。以下是一些常用的测试用例:
// 测试 Promise 的基本功能
it('should resolve with a value', () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello, world!');
}, 100);
});
return promise.then(value => {
expect(value).toBe('Hello, world!');
});
});
it('should reject with a reason', () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Something went wrong!'));
}, 100);
});
return promise.catch(reason => {
expect(reason.message).toBe('Something went wrong!');
});
});
it('should chain multiple then callbacks', () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello, world!');
}, 100);
});
return promise
.then(value => {
return value + '!';
})
.then(value => {
return value.toUpperCase();
})
.then(value => {
expect(value).toBe('HELLO, WORLD!');
});
});
总结
通过以上步骤,我们成功实现了一个完整的、能够通过所有测试用例的 Promise 类。通过深入了解 Promise 的内部结构和实现原理,我们不仅可以更好地理解 Promise 的用法,还可以灵活地应用 Promise 来构建更复杂、更可靠的异步应用。
常见问题解答
-
为什么需要 Promise?
Promise 允许我们以优雅、可控的方式处理异步操作,从而避免了回调地狱和金字塔式错误处理。 -
Promise 的三种状态是什么?
Pending、Fulfilled 和 Rejected。 -
如何注册回调函数来处理 Promise 的结果或错误?
使用 then 和 catch 方法。 -
Promise 可以被取消吗?
原生 Promise 无法被取消,但可以通过第三方库实现取消功能。 -
如何处理 Promise 链中的错误?
可以使用 catch 方法或 then 方法中的第二个参数。