for await…of

前面介绍过,for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。

  1. async function f() {
  2. for await (const x of createAsyncIterable(['a', 'b'])) {
  3. console.log(x);
  4. }
  5. }
  6. // a
  7. // b

上面代码中,createAsyncIterable()返回一个拥有异步遍历器接口的对象,for...of循环自动调用这个对象的异步遍历器的next方法,会得到一个 Promise 对象。await用来处理这个 Promise 对象,一旦resolve,就把得到的值(x)传入for...of的循环体。

for await...of循环的一个用途,是部署了 asyncIterable 操作的异步接口,可以直接放入这个循环。

  1. let body = '';
  2. async function f() {
  3. for await(const data of req) body += data;
  4. const parsed = JSON.parse(body);
  5. console.log('got', parsed);
  6. }

上面代码中,req是一个 asyncIterable 对象,用来异步读取数据。可以看到,使用for await...of循环以后,代码会非常简洁。

如果next方法返回的 Promise 对象被rejectfor await...of就会报错,要用try...catch捕捉。

  1. async function () {
  2. try {
  3. for await (const x of createRejectingIterable()) {
  4. console.log(x);
  5. }
  6. } catch (e) {
  7. console.error(e);
  8. }
  9. }

注意,for await...of循环也可以用于同步遍历器。

  1. (async function () {
  2. for await (const x of ['a', 'b']) {
  3. console.log(x);
  4. }
  5. })();
  6. // a
  7. // b

Node v10 支持异步遍历器,Stream 就部署了这个接口。下面是读取文件的传统写法与异步遍历器写法的差异。

  1. // 传统写法
  2. function main(inputFilePath) {
  3. const readStream = fs.createReadStream(
  4. inputFilePath,
  5. { encoding: 'utf8', highWaterMark: 1024 }
  6. );
  7. readStream.on('data', (chunk) => {
  8. console.log('>>> '+chunk);
  9. });
  10. readStream.on('end', () => {
  11. console.log('### DONE ###');
  12. });
  13. }
  14. // 异步遍历器写法
  15. async function main(inputFilePath) {
  16. const readStream = fs.createReadStream(
  17. inputFilePath,
  18. { encoding: 'utf8', highWaterMark: 1024 }
  19. );
  20. for await (const chunk of readStream) {
  21. console.log('>>> '+chunk);
  22. }
  23. console.log('### DONE ###');
  24. }