Skip to main content

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