参考:ECMAScript 6 入门
同步遍历器的问题
前面讲的遍历器调用next方法后会立即返回一个这样的对象{value: '随你是啥', done: false/true}
如果 yield 后面的表达式是同步的还好说,value 就是表达式的值;如果 yield 后面的表达式是异步的会怎样?调用next方法还是会立即返回像上面的那种对象,但是 value 的值是 promise 对象。
解决方案
为了解决这个问题,ES2018 引入了“异步遍历器”(Async Iterator),为异步操作提供原生的遍历器接口。
在异步遍历器中,next方法返回的是promise对象,在它的 resolve 结果中返回{value: 'resolve 的值', done: false/true}
对象
异步遍历的接口
同步遍历器的接口部署在Symbol.iterator
属性上面。
异步遍历器接口,部署在Symbol.asyncIterator
属性上面。
两种遍历接口遍历异步方法的不同
同步:iterator.next().value.then()
异步:asyncIterator.next().then()
for await...of
同步遍历器使用 for...of,异步遍历器使用 for await...of,举例
// 因为有 await,该方法还实现了异步方法的同步执行
async function f() {
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b
异步遍历接口的好处
for await...of
循环也可以用于同步遍历器。
异步遍历器的设计目的之一,就是处理同步操作和异步操作时,能够使用同一套接口。
异步 Generator 函数
就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。
在语法上,异步 Generator 函数就是async函数与 Generator 函数的结合。
async function* gen() {
yield 'hello';
yield 'world';
}
const genObj = gen();
genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }
genObj.next().then(x => console.log(x));
// { value: 'world', done: false }
在上面的例子中,因为是异步的,所以调用next方法时返回promise对象。另外两个next调用 then 时,不能保证哪个先执行(假设yield后面是执行完所用时间不确定的异步方法)
function fetchRandom() {
const url = 'https://www.random.org/decimal-fractions/'
+ '?num=1&dec=10&col=1&format=plain&rnd=new';
return fetch(url);
}
async function* asyncGenerator() {
console.log('Start');
const result = await fetchRandom(); // (A)
yield 'Result: ' + await result.text(); // (B)
console.log('Done');
}
const ag = asyncGenerator();
ag.next().then(({value, done}) => {
console.log(value);
})
上面代码中,ag是asyncGenerator函数返回的异步遍历器对象。调用ag.next()以后,上面代码的执行顺序如下。
- ag.next()立刻返回一个 Promise 对象。
- asyncGenerator函数开始执行,打印出Start。
- await命令返回一个 Promise 对象,asyncGenerator函数停在这里。
- A 处变成 fulfilled 状态,产生的值放入result变量,asyncGenerator函数继续往下执行。
- 函数在 B 处的yield暂停执行,一旦yield命令取到值,ag.next()返回的那个 Promise 对象变成 fulfilled 状态。
- ag.next()后面的then方法指定的回调函数开始执行。该回调函数的参数是一个对象{value, done},其中value的值是yield命令后面的那个表达式的值,done的值是false。
比对上面两个例子
可以发现,它们的调用方式都是异步遍历器对象.next().then()
。不同的是,没有await的,异步方法的执行顺序无法保证,有await的 ,按顺序执行异步方法。
网友评论