for await...of 异步迭代器

在 JavaScript 的异步编程发展历程中,ES2015 引入了 for...of 循环来处理同步迭代器,但对于异步数据源(如数据库查询、文件读取或网络请求)的处理仍然不够优雅。ES2018 (ES9) 通过 for await...of 语法填补了这一空白,为异步迭代提供了原生支持。

基本语法与工作原理

for await...of 循环的语法结构与 for...of 类似,但专门用于处理异步可迭代对象:

javascript 复制代码
for await (const item of asyncIterable) {
  // 处理每个异步获取的item
}

这个循环会:

  1. 调用异步可迭代对象的 [Symbol.asyncIterator]() 方法获取异步迭代器
  2. 重复调用迭代器的 next() 方法(返回 Promise)
  3. 等待每个 Promise 解析
  4. 将解析后的值赋给循环变量
  5. 执行循环体

创建异步可迭代对象

要使用 for await...of,我们需要创建或获取异步可迭代对象。常见方式包括:

  1. 异步生成器函数
javascript 复制代码
async function* asyncGenerator() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
  yield await Promise.resolve(3);
}
  1. 自定义异步迭代器
javascript 复制代码
const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      next() {
        if (i < 3) {
          return Promise.resolve({ value: i++, done: false });
        }
        return Promise.resolve({ done: true });
      }
    };
  }
};

实际应用场景

for await...of 在以下场景中特别有用:

  1. 处理分页API
javascript 复制代码
async function fetchAllPages(url) {
  let allResults = [];
  for await (const pageData of fetchPaginatedData(url)) {
    allResults = allResults.concat(pageData);
  }
  return allResults;
}
  1. 读取大文件
javascript 复制代码
import { createReadStream } from 'fs';

async function processFile(filePath) {
  const stream = createReadStream(filePath, { encoding: 'utf8' });
  for await (const chunk of stream) {
    console.log('Received chunk:', chunk.length);
  }
}
  1. 处理多个异步请求
javascript 复制代码
async function processRequests(urls) {
  for await (const response of urls.map(url => fetch(url))) {
    const data = await response.json();
    console.log(data);
  }
}

注意事项与最佳实践

  1. 错误处理:使用 try/catch 捕获异步迭代中的错误

    javascript 复制代码
    try {
      for await (const item of asyncIterable) {
        // ...
      }
    } catch (err) {
      console.error('迭代出错:', err);
    }
  2. 提前终止:可以使用 breakreturn 提前退出循环

  3. 性能考虑:异步迭代是顺序执行的,如需并行处理应考虑其他方法

  4. 浏览器兼容性:虽然现代浏览器都支持,但在旧环境中可能需要 polyfill

与相关技术的比较

  1. Promise.all 的区别

    • Promise.all 并行处理所有 Promise
    • for await...of 顺序处理每个 Promise
  2. for...of 的区别

    • for...of 用于同步迭代器
    • for await...of 用于异步迭代器
  3. async/await 的关系

    • 都是基于 Promise 的语法糖
    • for await...of 可以看作是 async/await 在迭代场景下的扩展

总结

ES9 引入的 for await...of 语法为 JavaScript 的异步编程提供了更强大的工具,使得处理异步数据流变得更加直观和简洁。无论是处理分页数据、文件流还是其他异步序列,这一特性都能显著提升代码的可读性和可维护性。随着 Node.js 和现代浏览器对异步迭代的全面支持,这一特性已成为现代 JavaScript 开发中不可或缺的一部分。