返回

异步数组迭代:突破 Symbol.iterator 的限制

前端

众所周知,Symbol.iterator 为数组提供了强大的迭代能力,使我们能够轻松地遍历其元素。然而,当我们遇到需要处理异步操作的数组时,Symbol.iterator 似乎遇到了局限。想象一个场景:数组中包含异步函数,我们希望在遍历过程中等待所有异步操作完成。此时,我们不禁思索:除了常见的 Promise.all 方法,是否还存在其他途径来实现这一需求?

跳出 Promise.all 的思维框架

Promise.all 作为一种可靠的解决方案,无疑可以满足我们的需求。它能够聚合多个 Promise,并在所有 Promise 完成后返回一个新的 Promise。然而,我们不妨跳出 Promise.all 的思维框架,探索更加灵活多变的实现方式。

利用 Generator 函数

Generator 函数为我们提供了另一种选择。它是一个特殊类型的函数,能够暂停和恢复执行。通过 Generator 函数,我们可以创建一种自定义的迭代器,允许我们逐个处理数组中的元素,并在每个异步操作完成后继续执行。

async function* asyncArrayIterator(array) {
  for (const element of array) {
    if (typeof element === 'function') {
      yield await element();
    } else {
      yield element;
    }
  }
}

在这个自定义的迭代器中,我们逐个遍历数组元素,判断它们是否是函数。如果是函数,则使用 await 等待其异步操作完成并返回结果。否则,直接返回元素本身。

应用 for await...of 循环

有了自定义迭代器,我们可以使用 for await...of 循环轻松地遍历数组并等待异步操作完成:

async function main() {
  const array = [
    async () => 1,
    async () => 2,
    3,
    async () => 4
  ];

  for await (const element of asyncArrayIterator(array)) {
    console.log(element);
  }
}

main();

自定义迭代器的优势

与 Promise.all 相比,自定义迭代器提供了以下优势:

  • 渐进处理: 自定义迭代器可以逐个处理元素,无需等待所有异步操作同时完成。
  • 取消支持: 如果不再需要剩余的异步操作,可以通过手动终止 Generator 函数来取消它们。
  • 灵活性: 自定义迭代器允许我们根据特定需求定制迭代逻辑,例如按需加载或跳过某些元素。

结语

通过利用 Symbol.iterator 和 Generator 函数,我们可以突破 Symbol.iterator 的局限,实现数组中元素的异步回调。自定义迭代器为我们提供了比 Promise.all 更灵活多变的解决方案,可以根据不同的场景和需求进行定制。