浅析异步循环for await of的使用及执行机制及for of/forEach本质区别和遇到异步时的处理
一、基本介绍
1、语法定义
for await...of
语句创建一个循环,该循环遍历异步可迭代对象以及同步可迭代对象,包括: 内置的 String
, Array
,类似数组对象 (例如 arguments
或 NodeList
),TypedArray
, Map
, Set
和用户定义的异步/同步迭代器。它使用对象的每个不同属性的值调用要执行的语句来调用自定义迭代钩子。
类似于 await
运算符一样,该语句只能在一个async function 内部使用。
2、如何使用
function getTime(seconds){
return new Promise(resolve=>{
setTimeout(() => {
console.log(seconds)
resolve(seconds)
}, seconds);
})
}
function test(){
let arr = [getTime(2000), getTime(300), getTime(1000)]
for (let x of arr){
console.log(x);
}
}
test()
对比区别,这里没有 await 。你可以猜测下这个会怎么输出?分别是 2000的Promise、300的Promise、1000的Promise 顺序输出的,然后再按时间延时打印 300、1000、2000
那么我们再想一下,如果我就是需要按 2000、300、1000 的顺序输出怎么办呢?
首先想到的是 async await 实现是可以的,但是会有缺陷。如果有好几个promise或者异步任务,就会写相应数量的 await,代码量变得庞大臃肿。所以使用 for await of 来实现
此时的输出就是 Promise undefined,然后过 2s 后,按 2000、300、1000 的顺序输出。
3、执行机制:for await of 循环可以暂停循环,当第一个异步执行完成后才会执行下一个,最后结果是让输出结果保持同步顺序输出。
什么意思呢?我们自己直接试试就知道了。
(1)上面代码 arr = [getTime(200), getTime(3000), getTime(1000)] 时,先打印 200,然后过 2s,再一起打印3000、1000
(2)[getTime(1000), getTime(5000), getTime(6000)] 时,先打印 1000,然后过4s,打印 5000,再过1s打印6000
二、async+await 遇见 forEach和 for···of
1、首先看 2 道题,自己先考虑下,能不能答对。
// 定义一个fetch函数模拟异步请求
function fetch(x) {
return new Promise((resolve, reject) => {
console.log('aaa');
setTimeout(() => {
resolve(x)
}, 500 * x)
})
}
// 第一题:
function test() {
let arr = [3, 2, 1]
arr.forEach(async item => {
const res = await fetch(item)
console.log(res)
})
console.log('end')
}
test(); // 输出什么
// 第二题:
async function test() {
let arr = [3, 2, 1]
for (const item of arr) {
const res = await fetch(item)
console.log(res)
}
console.log('end')
}
test(); // 输出什么
第一题:先同时先3个aaa和end,test函数执行完成返回undefined,然后异步回调每隔500ms依次打印1、2、3
第二题:先aaa,1500ms后打印 3,再 aaa,1s后打印2,再aaa,500ms后打印1,再end,再test函数返回值undefined。
这里我理解错了,test函数执行完成,就应该返回 Promise undefined,也就是第二题输出是这样的:
这里需要特别注意下 end 的打印,这里涉及到“协程”的概念理解,asyc await 本质是利用协程来实现的。
2、为什么同样是遍历,输出结果却不一样呢?
因为 for...of 内部处理的机制和 forEach 不同,forEach 是直接调用回调函数,for...of 是通过迭代器的方式去遍历。
3、两者的处理机制:
// 参考下 Polyfill 版本的 forEach,简化后的伪代码:
while (index < arr.length) {
callback(item, index) //我们传入的回调函数
}
而使用迭代器写第二题(既for...of 码的语法糖)等价于:
async function test() {
let arr = [3, 2, 1]
const iterator = arr[Symbol.iterator]() //for of会自动调用遍历器函数
let res = iterator.next()
while (!res.done) {
const value = res.value
const res1 = await fetch(value)
console.log(res1)
res = iterator.next()
}
console.log('end')
}