generator
让一个对象支持for...of
遍历
for...of
遍历方法一
const range = {
from: 0,
to: 10,
[Symbol.iterator]() {
return { // 第一次会使用current的值
current: this.from,
last: this.to,
next() { // 后续每次for...of都会调用next(),当done为true时候结束调用
if (this.current <= this.last) {
return {
done: false,
value: this.current++
}
}
return {
done: true
}
}
}
}
}
console.log([...range]) // (11) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
方法二
const rangeUsingGenerator = {
from: 0,
to: 10,
*[Symbol.iterator]() { // generator函数返回就是一个iterable,所以不需要写current ,next 那些
for (let value = this.from; value <= this.to; value++) yield value
}
}
console.log([...rangeUsingGenerator]) // output is same as before
Async Iterator
为了使对象可以异步迭代:
我们需要使用
Symbol.asyncIterator
取代Symbol.iterator
。next()
方法应该返回一个promise
。我们应该使用
for await (let item of iterable)
循环来迭代这样的对象
const asyncGenerator = {
from: 0,
to: 10,
[Symbol.asyncIterator]() { // (1)
return {
current: this.from,
last: this.to,
async next() { // (2)
if (this.current <= this.last) {
await new Promise((resolve) => setTimeout(resolve, 1000)) // (3)
return {
done: false,
value: this.current++
}
}
return {
done: true
}
}
}
}
}
const run = async () => {
for await (let value of asyncGenerator) { // (4)
console.log(value)
}
}
run()
正如我们所看到的,其结构与常规的 iterator 类似:
为了使一个对象可以异步迭代,它必须具有方法
Symbol.asyncIterator
(1)
。这个方法必须返回一个带有
next()
方法的对象,next()
方法会返回一个 promise(2)
。这个
next()
方法可以不是async
的,它可以是一个返回值是一个promise
的常规的方法,但是使用async
关键字可以允许我们在方法内部使用await
,所以会更加方便。这里我们只是用于延迟 1 秒的操作(3)
。我们使用
for await(let value of range)
(4)
来进行迭代,也就是在for
后面添加await
。它会调用一次range[Symbol.asyncIterator]()
方法一次,然后调用它的next()
方法获取值。
Iterator
Async Iterator
提供 iterator 的对象方法
Symbol.iterator
Symbol.asyncIterator
next()
返回的值是
任意值
Promise
要进行循环,使用
for..of
for await..of
Async generator
async function* asyncGenerator(from = 0, to) {
for (let i = from; i <= to; i++) {
await new Promise((resolve) => setTimeout(resolve, 1000))
yield i
}
}
const run = async () => {
for await (let value of asyncGenerator(0, 10)) {
console.log(value)
}
}
run()
Async Iterator
const asyncIteratorObj = {
from: 0,
to: 10,
async *[Symbol.asyncIterator]() {
for (let value = this.from; value <= this.to; value++) {
await new Promise((resolve) => setTimeout(resolve, 500))
yield value
}
}
}
const run = async () => {
for await (let value of asyncIteratorObj) {
console.log(value)
}
}
run()
总结
常规的 iterator 和 generator 可以很好地处理那些不需要花费时间来生成的的数据。
当我们期望异步地,有延迟地获取数据时,可以使用它们的 async counterpart,并且使用 for await..of
替代 for..of
。
Async iterator 与常规 iterator 在语法上的区别:
Iterable
Async Iterable
提供 iterator 的对象方法
Symbol.iterator
Symbol.asyncIterator
next()
返回的值是
{value:…, done: true/false}
resolve 成 {value:…, done: true/false}
的 Promise
Async generator 与常规 generator 在语法上的区别:
Generator
Async generator
声明方式
function*
async function*
next()
返回的值是
{value:…, done: true/false}
resolve 成 {value:…, done: true/false}
的 Promise
在 Web 开发中,我们经常会遇到数据流,它们分段流动(flows chunk-by-chunk)。例如,下载或上传大文件。
我们可以使用 async generator 来处理此类数据。值得注意的是,在一些环境,例如浏览器环境下,还有另一个被称为 Streams 的 API,它提供了特殊的接口来处理此类数据流,转换数据并将数据从一个数据流传递到另一个数据流(例如,从一个地方下载并立即发送到其他地方)。
Last updated
Was this helpful?