各位小伙伴们,今天我们来实现一个面试中经常会被问到的内容------手写实现一个简版的 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
。
js
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
。
js
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);
}
});
});
}
});
}
-
链式调用:
then
方法需要返回一个新的MyPromise
实例,以便支持链式调用。 -
回调处理: 当状态为
fulfilled
或rejected
时,立即执行对应的回调,并将resolve
或reject
的结果传递给下一个then
。 -
异步执行: 使用
setTimeout
模拟异步执行,确保回调函数是在resolve
或reject
之后才执行。 -
pending 状态处理: 当状态还是
pending
时,先将回调函数存储起来,等状态改变后再执行。
实现 catch
方法
catch
方法是 then
方法的一个变种,它只关注失败的情况。我们可以直接使用 then
方法来实现 catch
。
js
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
的完成。
使用示例:
js
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
需要处理以下几种情况:
- 接收的参数是一个可迭代对象(如数组)。
- 确保传入的每个元素都是
Promise
实例。 - 等待所有
Promise
都解决时,返回一个包含所有结果的数组。 - 如果其中任何一个
Promise
被拒绝,立即返回拒绝。
js
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
,在面试中轻松应对相关问题。如果这篇文章对你有帮助可以点个赞哦😊!