为什么要有异步迭代器
同步迭代器里数据都是当时就能获取的(没有延迟),而异步迭代器里的数据往往获取是需要时间的(有延迟)。
如果同步迭代器数据获取需要时间(比如实际场景中请求接口),那么再用 for-of
遍历的话,就有问题——控制不了数据的处理顺序。
1 2 3 4 5 6 7 8 9 10 11
| let obj = { *[Symbol.iterator]() { yield 1; yield 2; yield 3; } }
for (let item of obj) { console.log(item) }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const obj = { *[Symbol.iterator]() { yield new Promise(resolve => setTimeout(() => resolve(1), 5000)); yield new Promise(resolve => setTimeout(() => resolve(2), 2000)); yield new Promise(resolve => setTimeout(() => resolve(3), 500)); } }
console.log(Date.now()) for (let item of obj) { item.then(data => console.log(Date.now(), data)) }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let obj = { async *[Symbol.asyncIterator]() { yield new Promise(resolve => setTimeout(() => resolve(1), 2000)); yield new Promise(resolve => setTimeout(() => resolve(2), 1000)); yield new Promise(resolve => setTimeout(() => resolve(3), 500)); } }
async function test(){ console.log(Date.now()) for await (let item of obj) { console.log(Date.now(), item) } }
test()
|
注意,异步迭代器要声明在 [Symbol.asyncIterator]
属性里,使用 for-await-of
循环处理的。 最终效果是,对任务挨个处理,上一个任务等待处理完毕后,再进入下一个任务。
异步迭代器
与同步可迭代对象部署了 [Symbol.iterator]
属性不同的是,异步可迭代对象的标志是部署了 [Symbol.asyncIterator]
这个属性。
异步迭代器用来处理不能即时拿到数据的情况,还能保证最终的处理顺序等于遍历顺序,不过需要依次排队等待。
与同步迭代器 iterator
不同的是,在 asyncIterator
上调用 next
方法得到是一个 Promise 对象,其内部值是 { value: xx, done: xx }
的形式,类似于 Promise.resolve({ value: xx, done: xx })
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const obj = { async *[Symbol.asyncIterator]() { yield 1; yield 2; yield 3; } }
const asyncIterator = obj[Symbol.asyncIterator]()
asyncIterator.next().then(data => console.log(data)) asyncIterator.next().then(data => console.log(data)) asyncIterator.next().then(data => console.log(data)) asyncIterator.next().then(data => console.log(data))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const justjavac = { [Symbol.asyncIterator]: () => { const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`]; return { next: () => Promise.resolve({ done: items.length === 0, value: items.shift() }) } } } ;(async function(){ for await (const item of justjavac) { console.log(item) } })();
|
同步迭代器vs异步迭代器
Iterator
1 2 3 4 5 6 7 8 9 10 11 12
| interface Iterator { next(value) : IteratorResult; [optional] throw(value) : IteratorResult; [optional] return(value) : IteratorResult; }
interface IteratorResult { value : any; done : bool; }
|
Async Iterators
1 2 3 4 5 6 7 8 9 10 11 12
| interface AsyncIterator { next(value) : Promise<IteratorResult>; [optional] throw(value) : Promise<IteratorResult>; [optional] return(value) : Promise<IteratorResult>; }
interface IteratorResult { value : any; done : bool; }
|
异步迭代语句
for await...of
语句创建一个循环,该循环遍历 异步可迭代对象 以及 同步可迭代对象。该语句只能在可以使用 await
的上下文中使用 ,包括异步函数体内以及模块中。 也就是说 for-await-of
语句除了能用在异步可迭代对象上,还能用在同步可迭代对象上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| async function* asyncGenerator() { yield new Promise(resolve => setTimeout(() => resolve('First'), 3000)); yield new Promise(resolve => setTimeout(() => resolve('Second'), 2000)); yield new Promise(resolve => setTimeout(() => resolve('Third'), 1000)); }
async function test() { for await (let result of asyncGenerator()) { console.log(Date.now(), Date.now() - pre, result); } } const pre = Date.now(); test();
|
1 2 3 4 5 6 7 8 9 10 11 12
| const obj = { *[Symbol.iterator]() { yield 1 yield 2 yield 3 } }; (async () => { for await(const item of obj) { console.log(item) } })()
|
注意是顺序问题:
- 如果一个对象上同时部署了
[Symbol.asyncIterator]
和 [Symbol.iterator]
,那就会优先使用 [Symbol.asyncIterator]
生成的异步迭代器。这很好理解,因为 for-await-of
本来就是为异步迭代器而生的。
- 相反如果同时部署了两个迭代器,但使用的是
for-of
那么优先使用同步迭代器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const obj = { *[Symbol.iterator]() { yield 1 yield 2 yield 3 }, async *[Symbol.asyncIterator]() { yield 4 yield 5 yield 6 } }
;(async () => { for await(const item of obj) { console.log(item) } })()
for (const item of obj) { console.log(item) }
|
异步生成器函数
异步生成器函数与生成器函数类似,但有以下区别:
- 当被调用时,异步生成器函数返回一个对象,”
async generator
“,含有 3 个方法( next
, throw
,和return
),每个方法都返回一个 Promise,Promise 返回 { value, done }
。而普通生成器函数并不返回 Promise,而是直接返回 { value, done }
。 这会自动使返回的异步生成器对象具有异步迭代的功能。
- 允许使用
await
表达式和 for-await-of
语句。
- 修改了
yield*
的行为以支持异步迭代。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| async function* asyncGenerator() { let count = 3; while (count >= 1) { let delay = count * 1000; let result = await new Promise(resolve => setTimeout(() => resolve(`Result ${count}`), delay)); yield result; count--; } }
async function processData() { for await (let result of asyncGenerator()) { console.log(Date.now(), Date.now() - pre, result); } }
const pre = Date.now();
processData();
|