如何以顺序或并行方式运行异步循环?
在对循环进行异步处理之前,我想提醒您如何编写经典的同步循环。
同步循环
很久以前我也是这样写For循环:
for (var i=0; i < array.length; i++) {
var item = array[i];
// do something with item
}
这样写很好,速度很快,但是有很多可读性和维护问题。我们可以用它更好的版本:
array.forEach((item) => {
// do something with item
});
JS语言在开发中运行很快,我们还有更多的功能和新语法,其中一个是我最常用的 async/await。我现在也经常在用,有时在异步处理数组的里数据时会出现这个问题。
异步循环
如何在循环中使用 await ?让我们编写异步函数并 await 每个任务。
async function processArray(array) {
array.forEach(item => {
// define synchronous anonymous function
// IT WILL THROW ERROR!
await func(item);
})
}
这段代码会出现一个语法错误,因为我们不能在同步函数里使用 await 。“processArray”是一个异步函数,但是我们在这个匿名函数里用到的 forEach 是同步的。
1.不需要等待结果
如何修复之前的问题?我们可以这样异步定义匿名函数:
async function processArray(array) {
array.forEach(async (item) => {
await func(item);
})
console.log('Done!');
}
但是 forEach 不会等到所有的步骤都完成,它只会执行任务和执行下一步。作为证明我们可以这样写一个简单例子:
function delay() {
return new Promise(resolve => setTimeout(resolve, 300));
}
async function delayedLog(item) {
// notice that we can await a function
// that returns a promise
await delay();
console.log(item);
}
async function processArray(array) {
array.forEach(async (item) => {
await delayedLog(item);
})
console.log('Done!');
}
processArray([1, 2, 3]);
结果输出为:
Done!
1
2
3
如果不需要等结果这样写是ok的,但是在大多数案例里这不是个很好的逻辑。
2. 线性处理数组
要等待结果,我们应该返回到老式的 for 循环,但这一次为了更好的可读性我们可以使用现代写法 for..of。
sync function processArray(array) {
for (const item of array) {
await delayedLog(item);
}
console.log('Done!');
}
结果输出:
1
2
3
Done!
该代码将依次处理每一项。但是我们可以使用并行运行。
3.并行处理数组
我们可以稍微修改下代码然后并行运行:
async function processArray(array) {
// map array to promises
const promises = array.map(delayedLog);
// wait until all promises are resolved
await Promise.all(promises);
console.log('Done!');
}
这段代码将并行运行许多delayLog 任务。但是对于非常大的数组要小心(并行的任务太多对CPU或内存来说可能比较吃力)。
也不要混淆“并行”与真正的线程和并行。该代码不能保证真正的并行执行。这取决于您的 item函数(在本演示中是delayedLog)。网络请求、webworker 和其他一些任务可以并行执行。
感谢阅读!
网友评论