异步任务在遍历中的并发执行和串行执行

异步任务在遍历中的并发执行和串行执行

首先解释下什么叫并行和串行

串行:是指在执行多个任务时,各个任务按顺序执行,完成一个才能执行下一个

并行:是指多个任务可以在同一时间间隔内执行(不是同一时刻)

四大遍历(forEachforfor...infor...of

先下结论,异步任务在 forEach中是并发执行,而在 forfor...infor...of中是串行执行

证明结论

首先定义一个 sleep 函数用于遍历

1
2
3
4
5
6
7
8
const sleep = async (delay) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("in sleep")
resolve()
}, delay)
})
}

对于 forEach

1
2
3
4
5
6
// forEach
const arr = [1, 2, 3]
arr.forEach(async (item) => {
await sleep(1000)
console.log(item)
})

在这个例子中,通过 forEach 遍历执行异步任务 sleep,代码执行的结果是,一秒后,一次性输出以下内容。

可以看出这显然是并发执行的结果

1
2
3
4
5
6
7
// 同时输出
我是异步任务
1
我是异步任务
2
我是异步任务
3

对于 for 循环

1
2
3
4
5
6
7
8
9
// for of
const arr = [1, 2, 3]
async function fn() {
for (let i = 0; i < arr.length; i++) {
await sleep(1000)
console.log(i)
}
}
fn()

在这个例子中,通过 for 遍历执行异步任务 sleep,代码执行的结果是,每间隔一秒,依次输出 1,2,3

可以看出这显然是串行执行的结果

1
2
3
4
5
6
7
8
9
// 间隔一秒
我是异步任务
1
// 间隔一秒
我是异步任务
2
// 间隔一秒
我是异步任务
3

对于 for…of 和 for…in

输出结果和 for 循环一样,即一个完成后执行下一个

原因分析

为什么 forEach在执行异步任务的遍历时是 并发执行,和其他三个循环不一样呢?

我们来看下 forEach的实现

参考 MDN 上 forEachpolyfillMDN-Array.prototype.forEach(),简单来说,

1
2
3
4
5
6
7
Array.prototype.forEach = function (callback) {
// this represents our array
for (let index = 0; index < this.length; index++) {
// We call the callback for each entry
callback(this[index], index, this)
}
}

相当于 for 循环执行了这个异步函数,所以是并行执行,导致了一次性全部输出结果:

参考文章