问题描述
我们知道js的一些异步编程的解决方案,比如最基本的回调函数、es6新增的promise以及es7新增的async和await。对此不熟悉的朋友可以参看我之前写的以一篇异步编程博客:js异步编程。
现在提出一个更高阶的问题:
现在有一组异步请求rs,当rs中所有的异步操作执行完毕后再执行操作t。
案例代码:
const getNumbers = () => Promise.resolve([1, 2, 3]);
const multi = num => new Promise((resolve, reject) => {
setTimeout(() => {
if (num) {
resolve(num * num);
} else {
reject(new Error('num not specified'));
}
}, 1000);
});
解决方案
for循环
此方案的思路很简单:先通过getNumbers函数获取所需要的所有请求,然后遍历数组分别单独执行multi函数,等所有请求执行完毕后最后再执行操作t。
错误代码:
async function test() {
console.log("开始执行异步操作!");
const nums = await getNumbers();
nums.forEach(async x => {
const res = await multi(x)
console.log(res);
});
console.log("执行操作t!");
}
执行此代码的结果为:
为什么会出现这种情况?
原因简单描述为:forEach 的回调函数是一个异步函数,异步函数中包含一个 await 等待 Promise 返回结果,相当于 for 循环执行了这个异步函数,所以是并行执行。
解决方案很简单,将forEach改为原始的for循环即可。
正确代码:
async function test1() {
console.log("开始执行异步操作!");
const nums = await getNumbers();
for (let i = 0; i < nums.length; i++) {
const res = await multi(nums[i]);
console.log(res);
}
console.log("执行操作t!");
}
执行此代码的结果为:
并发请求
原生的js提供一个处理并发请求的方案:Promise.all()
此方案的思路:先通过getNumbers函数获取所需要的所有请求,然后遍历数组分别单独执行multi函数并获取所有的promise,使用Promise.all方法并发执行所有的promise,最后再执行操作t。
方案代码:
async function test2() {
console.log("开始执行异步操作!");
const nums = await getNumbers();
const promises = [];
nums.forEach(num => {
promises.push(multi(num));
});
// 两种等价写法,选择其一即可
const results = await Promise.all(promises);
console.log(results);
console.log("执行操作t!");
// Promise.all(promises).then(results => {
// console.log(results);
// console.log("执行操作t!");
// });
}
执行此代码的结果为:
小结
两种解决方案虽然都能解决一组异步请求的问题,但是其效果不完全一样,for循环的方式可以细致到精确处理一组异步请求里的每个请求,而Promise.all()只是一次性将一组异步请求全部处理并返回其结果。
结语
实际工作中可能会经常用到这种类似的并发请求问题,希望这篇博客能够帮助大家解答心中的疑惑。