Array.fromAsync()

基线 2024

新功能

2024 年 1 月起,此功能在所有最新的设备和浏览器版本中均可使用。此功能可能无法在较旧的设备或浏览器中使用。

Array.fromAsync() 静态方法从一个异步可迭代对象可迭代对象类数组 对象创建一个新的浅拷贝的 Array 实例。

语法

js
Array.fromAsync(arrayLike)
Array.fromAsync(arrayLike, mapFn)
Array.fromAsync(arrayLike, mapFn, thisArg)

参数

arrayLike

要转换为数组的异步可迭代对象、可迭代对象或类数组对象。

mapFn 可选

要对数组的每个元素调用的函数。如果提供,要添加到数组的每个值都将首先通过此函数传递,并且 mapFn 的返回值将添加到数组中(在 等待 之后)。该函数使用以下参数调用

element

正在数组中处理的当前元素。因为所有元素都将首先 等待,所以此值永远不会是 可执行 then 的对象

index

正在数组中处理的当前元素的索引。

thisArg 可选

在执行 mapFn 时用作 this 的值。

返回值

一个新的 Promise,其完成值为一个新的 Array 实例。

描述

Array.fromAsync() 允许您从以下对象创建数组:

Array.fromAsync() 以与 for await...of 非常相似的方式迭代异步可迭代对象。Array.fromAsync() 在行为方面几乎等效于 Array.from(),除了以下几点:

  • Array.fromAsync() 处理异步可迭代对象。
  • Array.fromAsync() 返回一个 Promise,该 Promise 完成为数组实例。
  • 如果 Array.fromAsync() 被调用时带有一个非异步可迭代对象,则要添加到数组的每个元素都将首先 等待
  • 如果提供 mapFn,则其输入和输出将在内部等待。

Array.fromAsync()Promise.all() 都可以将一个 Promise 的可迭代对象转换为一个数组的 Promise。但是,它们之间有两个关键区别:

  • Array.fromAsync() 依次等待从对象中产生的每个值。Promise.all() 并发等待所有值。
  • Array.fromAsync() 懒惰地迭代可迭代对象,并且在当前值结算之前不会检索下一个值。Promise.all() 会提前检索所有值并等待它们全部结算。

示例

从异步可迭代对象创建数组

js
const asyncIterable = (async function* () {
  for (let i = 0; i < 5; i++) {
    await new Promise((resolve) => setTimeout(resolve, 10 * i));
    yield i;
  }
})();

Array.fromAsync(asyncIterable).then((array) => console.log(array));
// [0, 1, 2, 3, 4]

从同步可迭代对象创建数组

js
Array.fromAsync(
  new Map([
    [1, 2],
    [3, 4],
  ]),
).then((array) => console.log(array));
// [[1, 2], [3, 4]]

从产生 Promise 的同步可迭代对象创建数组

js
Array.fromAsync(
  new Set([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]),
).then((array) => console.log(array));
// [1, 2, 3]

从 Promise 的类数组对象创建数组

js
Array.fromAsync({
  length: 3,
  0: Promise.resolve(1),
  1: Promise.resolve(2),
  2: Promise.resolve(3),
}).then((array) => console.log(array));
// [1, 2, 3]

使用 mapFn

mapFn 的输入和输出都由 Array.fromAsync() 在内部等待。

js
function delayedValue(v) {
  return new Promise((resolve) => setTimeout(() => resolve(v), 100));
}

Array.fromAsync(
  [delayedValue(1), delayedValue(2), delayedValue(3)],
  (element) => delayedValue(element * 2),
).then((array) => console.log(array));
// [2, 4, 6]

与 Promise.all() 的比较

Array.fromAsync() 依次等待从对象中产生的每个值。Promise.all() 并发等待所有值。

js
function* makeIterableOfPromises() {
  for (let i = 0; i < 5; i++) {
    yield new Promise((resolve) => setTimeout(resolve, 100));
  }
}

(async () => {
  console.time("Array.fromAsync() time");
  await Array.fromAsync(makeIterableOfPromises());
  console.timeEnd("Array.fromAsync() time");
  // Array.fromAsync() time: 503.610ms

  console.time("Promise.all() time");
  await Promise.all(makeIterableOfPromises());
  console.timeEnd("Promise.all() time");
  // Promise.all() time: 101.728ms
})();

对同步可迭代对象没有错误处理

for await...of 类似,如果正在迭代的对象是同步可迭代对象,并且在迭代过程中抛出错误,则不会调用底层迭代器的 return() 方法,因此迭代器不会关闭。

js
function* generatorWithRejectedPromises() {
  try {
    yield 0;
    yield Promise.reject(3);
  } finally {
    console.log("called finally");
  }
}

(async () => {
  try {
    await Array.fromAsync(generatorWithRejectedPromises());
  } catch (e) {
    console.log("caught", e);
  }
})();
// caught 3
// No "called finally" message

如果需要关闭迭代器,则需要使用 for...of 循环,并自行等待每个值。

js
(async () => {
  const arr = [];
  try {
    for (const val of generatorWithRejectedPromises()) {
      arr.push(await val);
    }
  } catch (e) {
    console.log("caught", e);
  }
})();
// called finally
// caught 3

规范

规范
ES Array.fromAsync (2022)
# sec-array.fromAsync

浏览器兼容性

BCD 表格仅在启用 JavaScript 的浏览器中加载。

另请参阅