Promise.all()

Promise.all() 静态方法采用可迭代的 Promise 作为输入并返回单个 Promise。当所有输入的 Promise 都满足时(包括传递空的可迭代对象时),返回的 Promise 就会满足,并带有一个满足值数组。当任何输入的 Promise 拒绝时,它会拒绝,并给出第一个拒绝原因。

¥The Promise.all() static method takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input's promises fulfill (including when an empty iterable is passed), with an array of the fulfillment values. It rejects when any of the input's promises rejects, with this first rejection reason.

Try it

语法

¥Syntax

js
Promise.all(iterable)

参数

¥Parameters

iterable

iterable(例如 Array)的 Promise。

返回值

¥Return value

Promise 是:

¥A Promise that is:

  • 已经满足了,如果传过来的 iterable 是空的。
  • 当给定 iterable 中的所有承诺都履行时,异步履行。履行值是履行值的数组,按照传递的承诺的顺序排列,无论完成顺序如何。如果传递的 iterable 非空但不包含待处理的 Promise,则返回的 Promise 仍会异步(而不是同步)实现。
  • 当给定 iterable 中的任何 Promise 拒绝时,异步拒绝。拒绝原因是第一个被拒绝的 Promise 的拒绝原因。

描述

¥Description

Promise.all() 方法是 承诺并发 方法之一。它对于聚合多个 Promise 的结果非常有用。当整个代码成功运行需要多个相关的异步任务时,通常会使用它 - 我们希望在代码继续执行之前完成所有这些任务。

¥The Promise.all() method is one of the promise concurrency methods. It can be useful for aggregating the results of multiple promises. It is typically used when there are multiple related asynchronous tasks that the overall code relies on to work successfully — all of whom we want to fulfill before the code execution continues.

一旦任何输入承诺被拒绝,Promise.all() 将立即拒绝。相比之下,Promise.allSettled() 返回的承诺将等待所有输入承诺完成,无论是否有一个拒绝。如果你需要输入可迭代中每个 Promise 的最终结果,请使用 allSettled()

¥Promise.all() will reject immediately upon any of the input promises rejecting. In comparison, the promise returned by Promise.allSettled() will wait for all input promises to complete, regardless of whether or not one rejects. Use allSettled() if you need the final result of every promise in the input iterable.

示例

¥Examples

使用 Promise.all()

¥Using Promise.all()

Promise.all 等待所有履行(或第一次拒绝)。

¥Promise.all waits for all fulfillments (or the first rejection).

js
const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 100);
});

Promise.all([p1, p2, p3]).then((values) => {
  console.log(values); // [3, 1337, "foo"]
});

如果 iterable 包含非承诺值,它们将被忽略,但仍计入返回的承诺数组值中(如果承诺已履行):

¥If the iterable contains non-promise values, they will be ignored, but still counted in the returned promise array value (if the promise is fulfilled):

js
// All values are non-promises, so the returned promise gets fulfilled
const p = Promise.all([1, 2, 3]);
// The only input promise is already fulfilled,
// so the returned promise gets fulfilled
const p2 = Promise.all([1, 2, 3, Promise.resolve(444)]);
// One (and the only) input promise is rejected,
// so the returned promise gets rejected
const p3 = Promise.all([1, 2, 3, Promise.reject(555)]);

// Using setTimeout, we can execute code after the queue is empty
setTimeout(() => {
  console.log(p);
  console.log(p2);
  console.log(p3);
});

// Logs:
// Promise { <state>: "fulfilled", <value>: Array[3] }
// Promise { <state>: "fulfilled", <value>: Array[4] }
// Promise { <state>: "rejected", <reason>: 555 }

Promise.all 的异步性还是同步性

¥Asynchronicity or synchronicity of Promise.all

下面的示例演示了当传递非空 iterablePromise.all 的异步性:

¥This following example demonstrates the asynchronicity of Promise.all when a non-empty iterable is passed:

js
// Passing an array of promises that are already resolved,
// to trigger Promise.all as soon as possible
const resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];

const p = Promise.all(resolvedPromisesArray);
// Immediately logging the value of p
console.log(p);

// Using setTimeout, we can execute code after the queue is empty
setTimeout(() => {
  console.log("the queue is now empty");
  console.log(p);
});

// Logs, in order:
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }

如果 Promise.all 拒绝,也会发生同样的情况:

¥The same thing happens if Promise.all rejects:

js
const mixedPromisesArray = [Promise.resolve(33), Promise.reject(44)];
const p = Promise.all(mixedPromisesArray);
console.log(p);
setTimeout(() => {
  console.log("the queue is now empty");
  console.log(p);
});

// Logs:
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "rejected", <reason>: 44 }

当且仅当传递的 iterable 为空时,Promise.all 才会同步解析:

¥Promise.all resolves synchronously if and only if the iterable passed is empty:

js
const p = Promise.all([]); // Will be immediately resolved
const p2 = Promise.all([1337, "hi"]); // Non-promise values are ignored, but the evaluation is done asynchronously
console.log(p);
console.log(p2);
setTimeout(() => {
  console.log("the queue is now empty");
  console.log(p2);
});

// Logs:
// Promise { <state>: "fulfilled", <value>: Array[0] }
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }

将 Promise.all() 与异步函数结合使用

¥Using Promise.all() with async functions

异步函数 中,对代码进行 "over-await" 是很常见的。例如,给定以下函数:

¥Within async functions, it's very common to "over-await" your code. For example, given the following functions:

js
function promptForDishChoice() {
  return new Promise((resolve, reject) => {
    const dialog = document.createElement("dialog");
    dialog.innerHTML = `
<form method="dialog">
  <p>What would you like to eat?</p>
  <select>
    <option value="pizza">Pizza</option>
    <option value="pasta">Pasta</option>
    <option value="salad">Salad</option>
  </select>
  <menu>
    <li><button value="cancel">Cancel</button></li>
    <li><button type="submit" value="ok">OK</button></li>
  </menu>
</form>
    `;
    dialog.addEventListener("close", () => {
      if (dialog.returnValue === "ok") {
        resolve(dialog.querySelector("select").value);
      } else {
        reject(new Error("User cancelled dialog"));
      }
    });
    document.body.appendChild(dialog);
    dialog.showModal();
  });
}

async function fetchPrices() {
  const response = await fetch("/prices");
  return await response.json();
}

你可以写一个像这样的函数:

¥You may write a function like this:

js
async function getPrice() {
  const choice = await promptForDishChoice();
  const prices = await fetchPrices();
  return prices[choice];
}

但请注意,promptForDishChoicefetchPrices 的执行并不依赖于彼此的结果。当用户选择菜品时,可以在后台获取价格,但在上面的代码中,await 运算符会导致异步函数暂停,直到做出选择,然后再次暂停,直到获取价格。我们可以使用 Promise.all 来同时运行它们,这样用户就不必在给出结果之前等待获取价格:

¥However, note that the execution of promptForDishChoice and fetchPrices don't depend on the result of each other. While the user is choosing their dish, it's fine for the prices to be fetched in the background, but in the code above, the await operator causes the async function to pause until the choice is made, and then again until the prices are fetched. We can use Promise.all to run them concurrently, so that the user doesn't have to wait for the prices to be fetched before the result is given:

js
async function getPrice() {
  const [choice, prices] = await Promise.all([
    promptForDishChoice(),
    fetchPrices(),
  ]);
  return prices[choice];
}

这里 Promise.all并发方法 的最佳选择,因为错误处理很直观 - 如果任何一个 Promise 被拒绝,结果就不再可用,所以整个 await 表达式都会抛出异常。

¥Promise.all is the best choice of concurrency method here, because error handling is intuitive — if any of the promises reject, the result is no longer available, so the whole await expression throws.

Promise.all 接受可迭代的 Promise,因此如果你使用它同时运行多个异步函数,则需要调用异步函数并使用返回的 Promise。直接将函数传递给 Promise.all 是行不通的,因为它们不是承诺。

¥Promise.all accepts an iterable of promises, so if you are using it to run several async functions concurrently, you need to call the async functions and use the returned promises. Directly passing the functions to Promise.all does not work, since they are not promises.

js
async function getPrice() {
  const [choice, prices] = await Promise.all([
    promptForDishChoice,
    fetchPrices,
  ]);
  // `choice` and `prices` are still the original async functions;
  // Promise.all() does nothing to non-promises
}

Promise.all 快速失败行为

¥Promise.all fail-fast behavior

如果任何元素被拒绝,则 Promise.all 被拒绝。例如,如果你传入四个在超时后解决的 Promise 和一个立即拒绝的 Promise,那么 Promise.all 将立即拒绝。

¥Promise.all is rejected if any of the elements are rejected. For example, if you pass in four promises that resolve after a timeout and one promise that rejects immediately, then Promise.all will reject immediately.

js
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("one"), 1000);
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("two"), 2000);
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("three"), 3000);
});
const p4 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("four"), 4000);
});
const p5 = new Promise((resolve, reject) => {
  reject(new Error("reject"));
});

// Using .catch:
Promise.all([p1, p2, p3, p4, p5])
  .then((values) => {
    console.log(values);
  })
  .catch((error) => {
    console.error(error.message);
  });

// Logs:
// "reject"

可以通过处理可能的拒绝来改变这种行为:

¥It is possible to change this behavior by handling possible rejections:

js
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p1_delayed_resolution"), 1000);
});

const p2 = new Promise((resolve, reject) => {
  reject(new Error("p2_immediate_rejection"));
});

Promise.all([p1.catch((error) => error), p2.catch((error) => error)]).then(
  (values) => {
    console.log(values[0]); // "p1_delayed_resolution"
    console.error(values[1]); // "Error: p2_immediate_rejection"
  },
);

规范

Specification
ECMAScript Language Specification
# sec-promise.all

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看