在 ECMAScript 2018( ES2018 ) 新功能 我们介绍了 TC39 会议已经票选出了 ES2018 的新功能
本着每一小节一个新功能的原则,本小节我们介绍另一个新功能,异步迭代
简介
使用 异步迭代 我们得到异步迭代器和异步迭代
异步迭代器跟常规的迭代器一样,除了除了它们的 next()
方法
异步迭代器 中的 next()
方法返回一个 {value,done}
对的 promise
为了使用异步迭代,我们需要在 for ...of
循环中使用 await
关键字
for await (const line of readLines(filePath)) { console.log(line); }
JavaScript 中的异步迭代器
概述和愿景
ECMAScript 2015 中引入的迭代器接口是一种顺序数据访问协议,是一种通用的,用于组合数据消费者和变换器的开发方法
迭代器的主要方法是 next()
,该方法返回一个 {value,done}
元组,其中 done
是一个布尔值,表示是否已到达迭代器的末尾,value
是序列中的生成值
因为迭代器的 next()
方法返回时必须知道序列中的下一个值和数据源的 「 完成 」状态,因此迭代器仅适用于呈现 同步 数据源
尽管我们 JavaScript 程序员遇到的许多数据源都是同步的 ( 例如内存列表和其他数据结构),但许多其他数据源却不同步。 例如,任何需要 I/O
访问的数据源通常使用基于事件或流的异步 API
来表示
遗憾的是,迭代器不能用于表示此类数据源
有人可能会说可以使用基于 promises
的迭代器,但这是远远不够的,因为 promises
只允许异步确定值,但却需要 同步 确定 「 完成 」 状态
为了为异步数据源提供通用数据访问协议,我们引入了 AsyncIterator
接口,异步迭代语句 ( for-await-of
) 和异步生成器函数
异步迭代器和异步迭代
异步迭代器很像普通的迭代器,除了它的 next()
返回 {value,done}
对的 promise
如上所述,异步迭代器的每个返回值都必须是 {value,done}
对的 promise
,因为迭代器方法返回时迭代器的下一个值和 「 完成 」状态可能都是未知的
const { value, done } = syncIterator.next(); asyncIterator.next().then(({ value, done }) => /* ... */);
此外,该提案还引入了一个新符号 Symbol.asyncIterator
,用于从给定对象获取异步迭代器
这允许任意对象通告它们是异步迭代,就像 Symbol.iterator
允许我们将其作为普通的同步可迭代进行通告一样
使用 Symbol.asyncIterator
最明显的例子就是 可读流
异步迭代器的概念隐含了请求队列的概念。 由于在解决先前请求的结果之前可能会多次调用迭代器方法,因此每个方法调用必须在内部排队,直到所有先前的请求操作都已完成
异步迭代器语句 for-await-of
for-await-of
语句是该提案引入的 for-of
迭代语句的变体,它迭代异步可迭代对象
一般的用法如下
for await (const line of readLines(filePath)) { console.log(line); }
注意 : 异步的
for-of
语句只允许在异步函数和异步生成器函数中使用
for-await-of
语句运行时,所需要的异步迭代器可以使用 [Symbol.asyncIterator]()
方法从数据源创建
而每次访问序列中的下一个值时,都会隐式使用 await
等待迭代器方法返回的 promise
异步生成器函数
异步生成器函数和普通的生成器函数相同,但有以下几点差别:
-
被调用时,异步生成器函数会返回一个对象,异步生成器的很多方法,比如
next()
、throw
和return
都会返回一个包含了{ value, done }
的promise
,而不是直接返回{ value, done }
,这会自动使返回的异步生成器对象成为异步迭代器 -
异步生成器函数中可以使用
await
表达式和for-await-of
语句 -
支持
yield *
委派给异步迭代
async function* readLines(path) { let file = await fileOpen(path); try { while (!file.EOF) { yield await file.readLine(); } } finally { await file.close(); } }
这个函数返回一个异步生成器对象,该对象可以与 for-await-of
一起使用,就像上面代码中演示的那样