JavaScript实现并发请求
· 阅读需 5 分钟
Promise.all
是 JavaScript 中用于并发处理多个 Promise
的方法,它会返回一个新的 Promise
,这个 Promise
只有在传入的所有 Promise
都被解决(fulfilled)时才会成功,并且返回一个包含所有结果的数组。如果其中任何一个 Promise
被拒绝(rejected),则 Promise.all
立即失败,并返回该被拒绝的原因。
接下来,我将解释 Promise.all
的具体实现逻辑,并给出一个简化的手写实现。
1. Promise.all
的核心逻辑
- 接收一个包含多个
Promise
的可迭代对象(通常是数组)。 - 并发地执行所有
Promise
。 - 等待所有
Promise
都成功后,返回一个包含所有结果的数组。 - 如果其中一个
Promise
被拒绝,立即返回拒绝的原因,并不会等待其他Promise
完成。
2. 手写实现 Promise.all
我们可以通过编写一个类似 Promise.all
的函数来理解它的工作原理:
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
// 如果传入的不是可迭代对象,直接抛出错误
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an iterable'));
}
const results = [];
let completedPromises = 0;
// 如果 promises 是空数组,立即 resolve 空数组
if (promises.length === 0) {
return resolve(results);
}
// 遍历每个 Promise
promises.forEach((promise, index) => {
// 将每个值转换为 Promise,以支持传入非 Promise 的值
Promise.resolve(promise)
.then((value) => {
// 保存结果到对应的 index
results[index] = value;
completedPromises += 1;
// 如果所有 Promise 都完成,resolve 最终结果
if (completedPromises === promises.length) {
resolve(results);
}
})
.catch((error) => {
// 如果任意一个 Promise 被拒绝,立即 reject 整个 Promise.all
reject(error);
});
});
});
}
3. Promise.all
手写实现解析
1. 检查传入参数:
- 首先,检查传入的参数是否是可 迭代对象(通常是数组)。如果不是,就直接拒绝(reject)返回。
2. 初始化结果存储和计数器:
results
:用于存储每个Promise
的解决值。completedPromises
:用于计数已完成的Promise
数量。
3. 处理空数组:
- 如果传入的是一个空数组,立即返回一个解决的
Promise
,结果是一个空数组。这是因为没有Promise
需要等待,所以可以直接返回结果。
4. 遍历每个 Promise
:
- 使用
forEach
遍历传入的Promise
数组。 - 对于每个
Promise
,使用Promise.resolve
将其转换为一个标准Promise
(这样即使传入的是非Promise
值,它也会被包装为Promise
)。 - 然后使用
then
方法处理成功的结果,将结果存储在results
数组中的对应位置,并增加计数器completedPromises
。 - 一旦所有
Promise
都成功,检查计数器是否等于promises.length
,如果是,则调用resolve
返回结果数组。
5. 处理失败的情况:
- 使用
catch
捕获每个Promise
的错误。 - 一旦有任意一个
Promise
失败(拒绝),立即调用reject
,并返回该错误。Promise.all
会立即停止执行,不会再等待其他Promise
。
4. 示例代码
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
setTimeout(resolve, 100, 'foo')
);
const promise3 = Promise.resolve(42);
myPromiseAll([promise1, promise2, promise3]).then((values) => {
console.log(values); // [3, 'foo', 42]
}).catch((error) => {
console.error('Error:', error);
});
在这个例子中,myPromiseAll
会并发执行 promise1
、promise2
和 promise3
。当它们都成功时,myPromiseAll
返回一个包含所有结果的数组 [3, 'foo', 42]
。
5. 注意事项
-
并发处理:
Promise.all
并不会按顺序执行Promise
,而是并发地启动所有Promise
。这意味着执行顺序取决于每个Promise
的完成时间,但结果数组的顺序与传入的Promise
顺序一致。 -
错误处理:一旦任意一个
Promise
被拒绝,Promise.all
会立即拒绝,并且不会等待其他Promise
继续执行。