文章中英模式
布鲁斯前端JS面试题目 - 手写 Promise.all 实现
学习如何手写实现 JavaScript Promise.all 方法,掌握处理多个异步任务的技巧,提升前端面试竞争力。
文章中英模式
懒得看文章?那就来看视频吧
Promise.all 方法概述
Promise.all
是 JavaScript 中处理多个 Promise 的重要方法,它接收一个 Promise 数组(或可迭代对象),并返回一个新的 Promise,该 Promise 在所有输入的 Promise 都成功解析时才会成功,或者在任一 Promise 被拒绝时立即拒绝。
Promise.all 的特性
- 1.
并行执行
:所有 Promise 同时开始执行 - 2.
等待全部
:等待所有 Promise 都完成 - 3.
一个失败则全失败
:任一 Promise 拒绝,整体立即拒绝 - 4.
保持结果顺序
:结果数组顺序与输入数组顺序一致 - 5.
非 Promise 值
:自动使用 Promise.resolve() 包装
手写实现 Promise.all
是前端面试中的热门题目,它可以检验对 Promise、异步处理和数组操作的理解。
原生 Promise.all 使用方式
在实现之前,先来看看 Promise.all
的原生使用方式:
// 创建三个 Promise
const promise1 = Promise.resolve(1);
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const promise3 = Promise.resolve(3);
// 使用 Promise.all 并行处理三个 Promise
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results); // [1, 2, 3] (顺序与输入数组相同)
})
.catch(error => {
console.error('至少有一个 Promise 被拒绝', error);
});
// 处理拒绝的例子
const promise4 = Promise.resolve(4);
const promise5 = Promise.reject('错误发生');
const promise6 = Promise.resolve(6);
Promise.all([promise4, promise5, promise6])
.then(results => {
console.log('不会执行到这里');
})
.catch(error => {
console.error(error); // '错误发生' (来自被拒绝的 promise5)
});
手写实现 Promise.all
现在让我们来实现 Promise.all
方法:
// 基本实现 Promise.all
function promiseAll(promises) {
// 检查输入是否为可迭代对象
if (!promises[Symbol.iterator]) {
return Promise.reject(new TypeError('参数必须是可迭代的'));
}
// 将输入转换为数组
const promiseArray = Array.from(promises);
const len = promiseArray.length;
// 创建一个新的 Promise
return new Promise((resolve, reject) => {
// 空数组直接返回空结果
if (len === 0) {
return resolve([]);
}
// 存储结果的数组
const results = new Array(len);
// 完成的 Promise 计数
let resolvedCount = 0;
// 处理每个 Promise
promiseArray.forEach((promise, index) => {
// 将非 Promise 值包装为 Promise
Promise.resolve(promise)
.then(value => {
// 按原顺序存储结果
results[index] = value;
resolvedCount++;
// 当所有 Promise 都完成时,解析整体 Promise
if (resolvedCount === len) {
resolve(results);
}
})
.catch(error => {
// 任一 Promise 被拒绝,整体 Promise 立即拒绝
reject(error);
});
});
});
}
让我们测试一下这个实现:
// 测试案例
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = 3; // 非 Promise 值,将被自动包装
promiseAll([p1, p2, p3])
.then(results => {
console.log(results); // [1, 2, 3]
})
.catch(error => {
console.error(error);
});
// 测试错误案例
const p4 = Promise.resolve(4);
const p5 = Promise.reject('失败');
const p6 = Promise.resolve(6);
promiseAll([p4, p5, p6])
.then(results => {
console.log('不会执行到这里');
})
.catch(error => {
console.error(error); // '失败'
});