Learn JavaScript

Promise.allSettled() - ожидание всех результатов промисов в JavaScript

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

Синтаксис

Базовая запись выглядит так:

Promise.allSettled(iterable)

Где:

  • iterable - итерируемый набор значений, чаще всего массив

Обычно в Promise.allSettled() передают массив промисов, но внутри могут быть и обычные значения.

Что возвращает Promise.allSettled()

Метод всегда возвращает объект Promise.

Если всё прошло штатно на уровне самого вызова, результатом будет массив объектов со статусом каждого элемента.

const promise = Promise.allSettled([
  Promise.resolve("ok"),
  Promise.reject(new Error("boom")),
]);

console.log(promise instanceof Promise); // true

promise.then((results) => {
  console.log(results);
});

Элементы результата имеют такой вид:

  • { status: "fulfilled", value: ... }
  • { status: "rejected", reason: ... }

Простой пример

Promise.allSettled([
  Promise.resolve("Пользователь"),
  Promise.reject(new Error("Ошибка загрузки")),
]).then((results) => {
  console.log(results);
});

Примерный результат:

[
  { status: "fulfilled", value: "Пользователь" },
  { status: "rejected", reason: Error("Ошибка загрузки") }
]

Это удобно, когда нужно получить картину по всем операциям сразу, а не падать на первой ошибке.

Promise.allSettled() ждёт все элементы до конца

Это главный смысл метода.

Даже если часть промисов завершится с ошибкой, итоговый результат всё равно будет только после завершения всех элементов.

const userPromise = Promise.resolve("Анна");
const settingsPromise = Promise.reject(new Error("Нет настроек"));

Promise.allSettled([userPromise, settingsPromise]).then((results) => {
  console.log(results);
});

То есть здесь не будет раннего падения, как у Promise.all().

Порядок результатов сохраняется по входному массиву

Это очень важный нюанс.

Promise.allSettled() не сортирует результат по скорости выполнения. Он сохраняет порядок именно таким, каким элементы были переданы.

const first = new Promise((resolve) => {
  setTimeout(() => resolve("first"), 50);
});

const second = new Promise((_, reject) => {
  setTimeout(() => reject("second error"), 10);
});

Promise.allSettled([first, second]).then((results) => {
  console.log(results[0].status); // "fulfilled"
  console.log(results[1].status); // "rejected"
});

Хотя второй элемент завершился раньше, в массиве он всё равно остался на второй позиции.

Обычные значения тоже работают

Это ещё один полезный момент.

В Promise.allSettled() можно передавать не только промисы, но и обычные значения.

Promise.allSettled([1, Promise.resolve(2), Promise.reject("x")]).then((results) => {
  console.log(results);
});

Примерный результат:

[
  { status: "fulfilled", value: 1 },
  { status: "fulfilled", value: 2 },
  { status: "rejected", reason: "x" }
]

Обычные значения будут считаться успешно завершёнными.

Ошибка внутри одного промиса не делает весь Promise.allSettled() отклонённым

Это ключевое отличие от Promise.all().

Promise.allSettled([
  Promise.resolve("ok"),
  Promise.reject(new Error("boom")),
  Promise.resolve("done"),
])
  .then((results) => {
    console.log(results);
  })
  .catch((error) => {
    console.log("Не выполнится", error);
  });

Здесь catch() из-за обычной ошибки одного элемента не нужен, потому что Promise.allSettled() всё равно вернёт массив результатов.

Как читать результат Promise.allSettled()

Это особенно полезная часть в реальном коде.

Сначала обычно проверяют status, а потом уже читают value или reason.

Promise.allSettled([
  Promise.resolve(1),
  Promise.reject(new Error("fail")),
  Promise.resolve(3),
]).then((results) => {
  for (const item of results) {
    if (item.status === "fulfilled") {
      console.log("Успех:", item.value);
    } else {
      console.log("Ошибка:", item.reason);
    }
  }
});

Такой подход безопаснее, чем пытаться читать value у каждого элемента подряд.

Как отделить успешные результаты от ошибок

Это один из самых частых практических сценариев.

Promise.allSettled([
  Promise.resolve(1),
  Promise.reject(new Error("fail")),
  Promise.resolve(3),
]).then((results) => {
  const successValues = results
    .filter((item) => item.status === "fulfilled")
    .map((item) => item.value);

  const errors = results
    .filter((item) => item.status === "rejected")
    .map((item) => item.reason);

  console.log(successValues); // [1, 3]
  console.log(errors); // [Error("fail")]
});

Это особенно удобно, когда часть результатов всё равно можно использовать, даже если остальные завершились с ошибкой.

Пустой массив даёт успешный результат []

Это полезно помнить.

Promise.allSettled([]).then((results) => {
  console.log(results); // []
});

Если передать пустой массив, результатом будет успешно завершённый промис с пустым массивом.

При этом обработчик then() всё равно выполнится асинхронно:

console.log("start");

Promise.allSettled([]).then((results) => {
  console.log(results); // []
});

console.log("end");

Результат будет таким:

start
end
[]

Чем Promise.allSettled() отличается от Promise.all()

Это сравнение стоит запомнить.

  • Promise.all() подходит, когда нужен полный успех всех элементов
  • Promise.allSettled() подходит, когда нужно дождаться вообще всех результатов, включая ошибки
Promise.all([
  Promise.resolve("ok"),
  Promise.reject(new Error("boom")),
]).catch((error) => {
  console.log("all:", error.message); // "boom"
});

Promise.allSettled([
  Promise.resolve("ok"),
  Promise.reject(new Error("boom")),
]).then((results) => {
  console.log("allSettled:", results);
});

Если ошибка одной операции не должна ломать весь сбор результатов, обычно удобнее именно Promise.allSettled().

Когда Promise.allSettled() всё же может завершиться с ошибкой

Это более редкий, но полезный нюанс.

Если проблема возникает не в самих промисах, а в переданном аргументе, итоговый промис может стать rejected.

Например:

Promise.allSettled(123).catch((error) => {
  console.log(error.name); // "TypeError"
});

Потому что метод ожидает итерируемый объект, а число им не является.

Когда это полезно

Promise.allSettled() особенно удобен, когда:

  • нужно получить результаты всех операций независимо от ошибок
  • часть данных можно использовать даже при частичном падении
  • вы хотите собрать успешные ответы и ошибки отдельно
  • важно показать пользователю полную картину, а не остановиться на первой ошибке

Пример:

Promise.allSettled([
  fetch("/api/user").then((response) => response.json()),
  fetch("/api/settings").then((response) => response.json()),
  fetch("/api/notifications").then((response) => response.json()),
]).then((results) => {
  console.log(results);
});

Такой подход особенно удобен, когда запросы независимы и неудача одного из них не должна скрывать остальные результаты.

Частые ошибки

Ожидать, что Promise.allSettled() упадёт на первой ошибке

Promise.allSettled([
  Promise.resolve("ok"),
  Promise.reject(new Error("boom")),
]).then((results) => {
  console.log(results);
});

Обычная ошибка одного элемента не делает итоговый промис отклонённым.

Пытаться читать value у каждого элемента без проверки status

Promise.allSettled([
  Promise.resolve(1),
  Promise.reject("x"),
]).then((results) => {
  console.log(results[1].value); // undefined
});

Сначала нужно смотреть на status, а уже потом читать value или reason.

Думать, что результаты приходят в порядке завершения

const first = new Promise((resolve) => {
  setTimeout(() => resolve("first"), 50);
});

const second = new Promise((resolve) => {
  setTimeout(() => resolve("second"), 10);
});

Promise.allSettled([first, second]).then((results) => {
  console.log(results[0].value); // "first"
  console.log(results[1].value); // "second"
});

Порядок зависит от входного массива, а не от скорости выполнения.

Думать, что обычные значения внутри массива недопустимы

Promise.allSettled([1, Promise.resolve(2)]).then((results) => {
  console.log(results);
});

Обычные значения допустимы и становятся элементами со статусом "fulfilled".

Передавать неитерируемое значение

Promise.allSettled(123).catch((error) => {
  console.log(error.name); // "TypeError"
});

Метод ожидает массив или другой итерируемый объект.

Быстрая памятка

  • Promise.allSettled() вызывается у Promise
  • метод всегда возвращает объект Promise
  • результатом обычно будет массив объектов со статусом
  • формат элемента: { status: "fulfilled", value: ... } или { status: "rejected", reason: ... }
  • порядок результата совпадает с порядком входных элементов
  • обычные значения внутри массива допустимы
  • метод ждёт завершения всех элементов
  • ошибка одного элемента не ломает весь результат
  • пустой массив даёт успешный результат []
  • перед чтением value или reason нужно проверять status

Коротко

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