for await...of
for await...of 语句创建一个循环,该循环遍历异步可迭代对象以及同步可迭代对象。
和 await
运算符一样,该语句只能在一个 async function 内部使用。
一、for await...of 的使用
举个例子,JS 的异步操作集合 promise
对象无法进行遍历:
function fn(time) {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve(time)
}, time)
})
}
function test() {
let arr = [fn(2000), fn(1000), fn(3000)]
for (let item of arr) {
console.log(Date.now(), item.then(console.log))
}
}
test()
// 1593692769003 Promise {<pending>}
// 1593692769004 Promise {<pending>}
// 1593692769004 Promise {<pending>}
// 1000
// 2000
// 3000
那么,JS 的异步操作集合要如何遍历呢?
将 test 函数前面添加 async
,并在 for-of 遍历的时候给每个元素前面添加 await
,每个对象等待结束之后再执行下一个,例如:
function fn(time) {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve(time)
}, time)
})
}
async function test() {
let arr = [fn(2000), fn(1000), fn(3000)]
for (let item of arr) {
console.log(Date.now(), await item.then(console.log))
}
}
test()
// 2000
// 1593693094315 undefined
// 1000
// 1593693096667 undefined
// 3000
// 1593693096667 undefined
上面代码的分析如下:
- 先执行了
await
后面的 then,返回2000
。 - 再执行 console.log 返回时间戳。
- 然后
await
的返回值是空,所以返回undefined
。 - 虽然
await
让 for-of 暂停了,但执行的顺序和我们想要的还是不一样。
可以使用 For await of 进行改进,在 test 函数前添加 async
关键字:
function fn(time) {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve(time)
}, time)
})
}
async function test() {
let arr = [fn(2000), fn(100), fn(3000)]
for await (let item of arr) {
console.log(Date.now(), item)
}
}
test()
// 1593693827422 2000
// 1593693827422 100
// 1593693828419 3000
二、for-of 和 for await of 的区别
- for-of 是用来遍历同步操作的。
- for-of 里面用
await
,如果有其他操作,也会有输出顺序错误。 for await of
是可以对异步集合进行操作。
三、如何实现自定义数据结构的异步遍历
- 定义一个对象,里面有基础数据类型,还有
promise
类型。 - 自定义一个
[Symbol.asyncIterator]
方法让其变成可遍历的对象。 - 使用
for await of
方法进行遍历。
const obj = {
count: 0,
fn(time) {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve({ done: false, value: time })
}, time)
})
},
[Symbol.asyncIterator]() {
let self = this
return {
next() {
self.count++
if (self.count < 4) {
return self.fn(Math.random() * 1000) // retuen 的返回值是 fn 函数的 resolve 返回的对象
} else {
return Promise.resolve({
done: true,
value: ''
})
}
}
}
}
}
async function test() {
for await (let item of obj) {
console.log(Date.now(), item)
}
}
test()
// 1597049702437 292.73328473812523
// 1597049702574 136.72074104961163
// 1597049703250 675.518962144079