结论 先说结论:
forEach
是异步执行
for-in
、for-of
是同步执行
forEach 是异步执行的 借用网上一个例子:
我们写代码的时候,希望代码能按照arr
数组的顺序执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function handle (x ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve (x) }, 1000 * x) }) } function test ( ) { let arr = [3 , 2 , 1 ] arr.forEach (async item => { const res = await handle (item) console .log (res) }) console .log ('结束' ) } test ()
我们更希望上边的实行结果为:
但是,真实情况是
实现一个 forEach 我们自己简单polyfill
一个 forEach
,用for
循环实现(据说查mdn
源代码是用while
循环实现,不过都一样):
1 2 3 4 5 6 const myForEach = function (fn ) { let i for (i=0 ; i<this .length ; i++){ fn (this [i], i) } }
这里的fn
函数就是我们例子中的 async/await
方法,forEach
直接执行了fn()
,而没有 await 就开始了下一次循环,或者换句话说,直接执行了Promise的构造函数
部分代码,而promise.then
代码没有被等待,就开始了下一次循环(执行下一个Promise构造函数
的代码),而之前的promise.then
都进入了事件队列
,等循环结束后,再把所有事件队列
里的任务拿出来执行,这就相当于同时发出去了好几个异步任务,哪个异步任务先执行完取决于自己执行多久。
比如,我们把数组的顺序let arr = [3, 2, 1]
,改为let arr = [3, 1, 2]
,再在构造函数里加一个console.log('执行了构造函数', x)
,这样就能清楚的看到上面的解释了,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function handle (x ) { return new Promise ((resolve, reject ) => { console .log ('执行了构造函数' , x); setTimeout (() => { resolve (x) }, 1000 * x) }) } function test ( ) { let arr = [3 , 1 , 2 ] arr.forEach (async item => { const res = await handle (item) console .log (res) }) console .log ('结束' ) } test ();
看执行结果你就懂了
for of 是同步执行的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function handle (x ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve (x) }, 1000 * x) }) } async function test ( ) { let arr = [3 , 2 , 1 ] for (const item of arr) { const res = await handle (item) console .log (res) } console .log ('结束' ) } test ()
结果:
因为for...of
并不像forEach
那么简单粗暴的方式去遍历执行,而是采用一种特别的手段——迭代器
去遍历。
1 2 3 4 5 6 7 8 9 10 11 12 let arr = [3 , 2 , 1 ];let iterator = arr[Symbol .iterator ]();console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());
好,现在我们把 iterator 用一到我们最开始的代码中;如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function handle (x ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve (x) }, 1000 * x) }) } async function test ( ) { let arr = [3 , 2 , 1 ] let iterator = arr[Symbol .iterator ](); let res = iterator.next (); while (!res.done ) { let value = res.value ; console .log (value); await handle (value); res = iterator.next (); } console .log ('结束' ) } test ()
打印一下结果:
参考:
forEach 和 for of 的执行异步顺序问题
forEach 同/异步问题