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
函数停在这里。result
变量,asyncGenerator
函数继续往下执行。yield
暂停执行,一旦yield
命令取到值,ag.next()
返回的那个 Promise 对象变成 fulfilled 状态。ag.next()
后面的then
方法指定的回调函数开始执行。该回调函数的参数是一个对象{value, done}
,其中value
的值是yield
命令后面的那个表达式的值,done
的值是false
。A 和 B 两行的作用类似于下面的代码。
return new Promise((resolve, reject) => {
fetchRandom()
.then(result => result.text())
.then(result => {
resolve({
value: 'Result: ' + result,
done: false,
});
});
});
如果异步 Generator 函数抛出错误,会导致 Promise 对象的状态变为reject
,然后抛出的错误被catch
方法捕获。
async function* asyncGenerator() {
throw new Error('Problem!');
}
asyncGenerator()
.next()
.catch(err => console.log(err)); // Error: Problem!
注意,普通的 async 函数返回的是一个 Promise 对象,而异步 Generator 函数返回的是一个异步 Iterator 对象。可以这样理解,async 函数和异步 Generator 函数,是封装异步操作的两种方法,都用来达到同一种目的。区别在于,前者自带执行器,后者通过for await...of
执行,或者自己编写执行器。下面就是一个异步 Generator 函数的执行器。
async function takeAsync(asyncIterable, count = Infinity) {
const result = [];
const iterator = asyncIterable[Symbol.asyncIterator]();
while (result.length < count) {
const {value, done} = await iterator.next();
if (done) break;
result.push(value);
}
return result;
}
上面代码中,异步 Generator 函数产生的异步遍历器,会通过while
循环自动执行,每当await iterator.next()
完成,就会进入下一轮循环。一旦done
属性变为true
,就会跳出循环,异步遍历器执行结束。
下面是这个自动执行器的一个使用实例。
async function f() {
async function* gen() {
yield 'a';
yield 'b';
yield 'c';
}
return await takeAsync(gen());
}
f().then(function (result) {
console.log(result); // ['a', 'b', 'c']
})
异步 Generator 函数出现以后,JavaScript 就有了四种函数形式:普通函数、async 函数、Generator 函数和异步 Generator 函数。请注意区分每种函数的不同之处。基本上,如果是一系列按照顺序执行的异步操作(比如读取文件,然后写入新内容,再存入硬盘),可以使用 async 函数;如果是一系列产生相同数据结构的异步操作(比如一行一行读取文件),可以使用异步 Generator 函数。
异步 Generator 函数也可以通过next
方法的参数,接收外部传入的数据。
const writer = openFile('someFile.txt');
writer.next('hello'); // 立即执行
writer.next('world'); // 立即执行
await writer.return(); // 等待写入结束
上面代码中,openFile
是一个异步 Generator 函数。next
方法的参数,向该函数内部的操作传入数据。每次next
方法都是同步执行的,最后的await
命令用于等待整个写入操作结束。
最后,同步的数据结构,也可以使用异步 Generator 函数。
async function* createAsyncIterable(syncIterable) {
for (const elem of syncIterable) {
yield elem;
}
}
上面代码中,由于没有异步操作,所以也就没有使用await
关键字。