- 作者:老汪软件技巧
- 发表时间:2024-10-16 17:02
- 浏览量:
各位小伙伴们,今天我们来实现一个面试中经常会被问到的内容——手写实现一个简版的 Promise。通过这个过程,不仅能够加深大家对 Promise 的理解,还能掌握其底层机制。
在这篇文章中,我们将一步步手写一个自定义的 MyPromise,通过代码讲解,帮助大家深入理解 Promise 的执行逻辑。我们将从基础的 Promise 定义、状态管理、then 方法的实现、回调函数收集等方面入手,逐步构建一个符合 Promise/A+ 规范的 Promise。
什么是 Promise?
首先,简单介绍一下 Promise。它是 JavaScript 用来处理异步操作的一种方式,它可以将异步代码转换为类似同步的形式。Promise 有三种状态:
pending(等待中):初始状态,既没有被解决也没有被拒绝。fulfilled(已完成):操作成功完成。rejected(已拒绝):操作失败。
一个 Promise 的状态一旦从 pending 变为 fulfilled 或 rejected,就不能再次更改。
接下来,我们开始手写一个 Promise 类,命名为 MyPromise。
构建基础的 MyPromise 类
我们先从基本的构造函数入手。Promise 的构造函数接收一个执行器(executor),executor 是一个包含两个参数 resolve 和 reject 的函数。我们需要在构造函数中定义 state 来表示 Promise 的当前状态,并且初始状态应该是 pending。
class MyPromise {
constructor(executor) {
// 定义初始状态为 pending
this.state = 'pending';
// 保存 resolve 和 reject 的值
this.value = undefined;
this.reason = undefined;
// 用于存储 .then 和 .catch 中的回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// 定义 resolve 函数
const resolve = (val) => {
if (this.state === 'pending') {
// 将状态改为 fulfilled
this.state = 'fulfilled';
// 保存 resolve 的值
this.value = val;
// 依次执行 .then 中的回调
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
// 定义 reject 函数
const reject = (val) => {
if (this.state === 'pending') {
// 将状态改为 rejected
this.state = 'rejected';
// 保存 reject 的值
this.reason = val;
// 依次执行 .catch 中的回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 调用 executor,并传入 resolve 和 reject
executor(resolve, reject);
}
}
我们在构造函数里定义了 this.state,初始值为 pending。resolve 函数的作用是,当调用时将 state 从 pending 变为 fulfilled,并保存传入的 val。
onFulfilledCallbacks 和 onRejectedCallbacks 是用来存储 then 和 catch 中的回调函数,当 resolve 或 reject 被调用时依次执行。
实现 then 方法
接下来,我们实现 then 方法,它接收两个回调函数——一个是成功时执行的 onFulfilled,另一个是失败时执行的 onRejected。
then(onFulfilled, onRejected) {
// 检查 onFulfilled 和 onRejected 是否为函数
if (onFulfilled && typeof onFulfilled !== 'function' || onRejected && typeof onRejected !== 'function') {
throw new Error('then 必须接受一个函数');
}
// 返回一个新的 MyPromise 实例,实现链式调用
return new MyPromise((resolve, reject) => {
// 如果当前状态是 fulfilled,则执行 onFulfilled 回调
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const result = onFulfilled(this.value); // 传入 resolve 的值
resolve(result); // 返回的值传递给下一个 then
} catch (error) {
reject(error); // 如果出错,捕获并传递给下一个 catch
}
});
}
// 如果当前状态是 rejected,则执行 onRejected 回调
if (this.state === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason); // 传入 reject 的原因
resolve(result);
} catch (error) {
reject(error);
}
});
}
// 如果当前状态是 pending,则将回调存起来,等到状态改变后再执行
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
});
});
}
});
}
实现 catch 方法
catch 方法是 then 方法的一个变种,它只关注失败的情况。我们可以直接使用 then 方法来实现 catch。
catch(onRejected) {
return this.then(null, onRejected);
}
catch 方法只处理 rejected 状态,因此它只传入 onRejected 回调,将 onFulfilled 设置为 null。
Promise.all
Promise.all 是一个静态方法,接收一个可迭代对象(如数组),这个对象的每个元素都是 Promise。它的主要作用是:
并行执行多个 Promise。当所有 Promise 都完成(即全部解决)时,返回一个新的 Promise,这个新的 Promise 的结果是一个包含每个 Promise 结果的数组,顺序与传入的数组顺序一致。如果其中任何一个 Promise 被拒绝,则 Promise.all 立即返回被拒绝的 Promise,并且不会等待其他 Promise 的完成。
使用示例:
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values); // 输出: [3, 42, "foo"]
});
Promise.all 与其他 Promise 方法的区别
在 Promise API 中,除了 Promise.all 之外,还有几个常用的静态方法,例如 Promise.race、Promise.any 和 Promise.allSettled。它们的主要区别如下:
Promise.all:等到所有 Promise 都成功才返回结果,如果其中任何一个 Promise 被拒绝,则整个操作失败。Promise.race:只要数组中任何一个 Promise 首先完成(无论是解决还是拒绝),Promise.race 就会返回该 Promise 的结果。Promise.any:只要数组中任何一个 Promise 首先解决,Promise.any 就会返回该解决值。只有当所有 Promise 都被拒绝时,它才会返回拒绝。Promise.allSettled:等待所有 Promise 都解决或拒绝,返回的结果数组包含每个 Promise 的状态和结果,无论成功或失败。手动实现Promise.all
现在我们来实现 Promise.all 的核心逻辑,帮助我们深入理解其工作原理。Promise.all 需要处理以下几种情况:
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
// 检查传入参数是否为数组
if (!Array.isArray(promises)) {
return reject(new TypeError('参数必须是数组'));
}
// 用于保存每个 Promise 的结果
let results = [];
// 记录已完成的 Promise 数量
let computed = 0;
// 总的 Promise 数量
const total = promises.length;
// 如果传入的数组为空,直接返回已解析的 Promise
if (total === 0) {
return resolve(results);
}
// 遍历每一个传入的 Promise
promises.forEach((promise, index) => {
// 使用 Promise.resolve 确保即使传入的不是 Promise 也能处理
Promise.resolve(promise)
.then(value => {
// 将每个 Promise 结果保存到对应的索引中
results[index] = value;
computed++;
// 当所有 Promise 都已解决,返回最终结果
if (computed === total) {
resolve(results);
}
})
.catch(error => {
// 如果有任何一个 Promise 被拒绝,立即返回失败的 Promise
reject(error);
});
});
});
}
总结
到这里,我们已经实现了一个简单版的 Promise。通过自定义 MyPromise,我们了解了 Promise 的核心原理以及如何处理异步逻辑、状态管理和回调机制。希望这篇文章能够帮助大家更好地理解 Promise,在面试中轻松应对相关问题。如果这篇文章对你有帮助可以点个赞哦!