Spec-Zone.ru › JavaScript

Promise.all()

Базовый уровень Широко доступно

Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна во всех браузерах с июля 2015 года.

Статический метод Promise.all() принимает итерируемый объект обещаний (промисов) в качестве входных данных и возвращает единый Promise. Это возвращаемое обещание исполняется, когда исполняются все входные обещания (включая случай, когда передан пустой итерируемый объект), с массивом значений исполнения. Оно отклоняется, когда отклоняется любое из входных обещаний, с указанием этой первой причины отклонения.

Попробуйте

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "foo");
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// Expected output: Array [3, 42, "foo"]

Синтаксис

Promise.all(iterable)

Параметры

iterable
Итерируемый объект (iterable) (такой как Array) обещаний. Эти значения ожидаются, поэтому другие thenable-объекты также разрешаются, а не-thenable-объекты возвращаются как есть.

Возвращаемое значение

Promise, который:

  • Уже исполнен, если переданный iterable пуст.
  • Асинхронно исполнен, когда исполняются все обещания в переданном iterable. Значение исполнения — это массив значений исполнения, в том порядке, в котором были переданы обещания, независимо от порядка их завершения. Если переданный iterable не пуст, но не содержит ожидающих обещаний, возвращаемое обещание всё равно исполняется асинхронно (вместо синхронного).
  • Асинхронно отклонён, когда отклоняется любое из обещаний в переданном iterable. Причина отклонения — это причина отклонения первого отклоненного обещания.

Описание

Метод Promise.all() является одним из методов конкурентной работы с обещаниями. Он может быть полезен для агрегирования результатов нескольких обещаний. Обычно он используется, когда есть несколько связанных асинхронных задач, от успешной работы которых зависит общий код — и мы хотим, чтобы все они были исполнены до того, как продолжится выполнение кода.

Promise.all() немедленно отклонится при отклонении любого из входных обещаний. В сравнении, обещание, возвращаемое Promise.allSettled(), будет ожидать завершения всех входных обещаний, независимо от того, отклонилось ли какое-либо из них. Используйте allSettled(), если вам нужен окончательный результат каждого обещания во входном итерируемом объекте.

Как и другие комбинаторы промисов, Promise.all() немедленно помечает все обещания как "обработанные" при вызове (путем вызова их методов .then()). Последующие отклонения после первого отклонения будут проигнорированы и не вызовут никаких событий unhandledrejection.

Отклонение возвращаемого обещания не отменяет оставшиеся операции и не отписывает обработчики, прикрепленные к их обещаниям. Многократная передача долгоживущего ожидающего обещания в Promise.all() может привести к накоплению обработчиков на этом обещании, даже если другой ввод отклоняется каждый раз.

Примеры

Использование Promise.all()

Promise.all ожидает всех исполнений (или первого отклонения).

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 содержит значения, не являющиеся обещаниями, они будут проигнорированы, но всё равно учтены в возвращаемом значении массива обещаний (если обещание исполнено):

// 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(new Error("bad"))]);

// 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>: Error: bad }

Деструктуризация результата

Вы найдёте деструктуризацию очень полезной, если вы группируете известное количество задач.

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

// With await
const [a, b, c] = await Promise.all([p1, p2, p3]);

Будьте осторожны: если порядок исходных обещаний и порядок переменных результата не совпадают, вы можете столкнуться с трудноуловимыми ошибками. Метод Promise.allKeyed() решает именно эту проблему.

Асинхронность или синхронность Promise.all

Следующий пример демонстрирует асинхронность Promise.all, когда передан непустой iterable:

// 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 отклоняется:

const mixedPromisesArray = [
  Promise.resolve(33),
  Promise.reject(new Error("bad")),
];
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>: Error: bad }

Promise.all разрешается синхронно тогда и только тогда, когда переданный iterable пуст:

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() с асинхронными функциями

Внутри асинхронных функций очень часто происходит "чрезмерное ожидание" вашего кода. Например, даны следующие функции:

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 canceled dialog"));
      }
    });
    document.body.appendChild(dialog);
    dialog.showModal();
  });
}

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

Вы можете написать функцию так:

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

Однако обратите внимание, что выполнение promptForDishChoice и fetchPrices не зависит от результатов друг друга. Пока пользователь выбирает свое блюдо, цены могут быть получены в фоновом режиме, но в приведенном выше коде оператор await заставляет асинхронную функцию приостановиться, пока не будет сделан выбор, а затем снова, пока не будут получены цены. Мы можем использовать Promise.all для их конкурентного выполнения, чтобы пользователю не приходилось ждать получения цен до того, как будет предоставлен результат:

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

Promise.all является лучшим выбором метода конкурентности здесь, поскольку обработка ошибок интуитивно понятна — если любое из обещаний отклоняется, результат становится недоступным, поэтому всё выражение await генерирует исключение.

Promise.all принимает итерируемый объект обещаний, поэтому, если вы используете его для конкурентного запуска нескольких асинхронных функций, вам необходимо вызвать асинхронные функции и использовать возвращенные обещания. Прямая передача функций в Promise.all не работает, поскольку они не являются обещаниями.

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
}

Обратите внимание, что следующий код, несмотря на другие нежелательные аспекты (например, создание большего количества переменных и склонную к ошибкам обработку ошибок), также обеспечивает конкурентность:

async function getPrice() {
  // Fire all async operations upfront
  const choicePromise = promptForDishChoice();
  const pricesPromise = fetchPrices();

  // Wait for each promise (all in-progress async operations keep running)
  const choice = await choicePromise;
  const prices = await pricesPromise;
}

Поведение Promise.all "быстрый отказ" (fail-fast)

Promise.all отклоняется, если какой-либо из элементов отклоняется. Например, если вы передадите четыре обещания, которые разрешаются после тайм-аута, и одно обещание, которое отклоняется немедленно, то Promise.all отклонится немедленно.

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"

Это поведение можно изменить, обработав возможные отклонения:

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"
  },
);

Спецификации

Спецификация
Спецификация языка ECMAScript® 2027
# sec-promise.all

Совместимость с браузерами

Десктоп Мобильные Сервер
Chrome Edge Firefox Opera Safari Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet WebView Android WebView on iOS Bun Deno Node.js
all
32
12
29
19
8
32
29
19
8
2.0
4.4.3
8
1.0.0
1.0
0.12.0

См. также

  • Promise
  • Promise.allKeyed()
  • Promise.allSettled()
  • Promise.any()
  • Promise.race()

© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API