Promise.allSettled: Гибкая обработка промисов с TypeScript

Работа с асинхронным кодом в JavaScript и TypeScript часто связана с необходимостью обработки множества промисов. Иногда нам нужно не только дождаться выполнения всех промисов, но и разделить их результаты на успешные и отклонённые. В этом случае на помощь приходит метод Promise.allSettled, который возвращает статусы всех переданных промисов: fulfilled (успешно выполненные) или rejected (отклонённые).

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

// Проверяет, является ли результат промиса отклонённым (rejected).
const isPromiseRejected = (input: PromiseSettledResult<unknown>): input is PromiseRejectedResult => input.status === 'rejected';

// Проверяет, является ли результат промиса выполненным (fulfilled).
const isPromiseFulfilled = <T>(input: PromiseSettledResult<T>): input is PromiseFulfilledResult<T> => input.status === 'fulfilled';

// Определяем асинхронную функцию, которая завершится успешно или с ошибкой в зависимости от аргумента.
const testPromise = async (shouldResolve: boolean) => {
  if (shouldResolve) return Promise.resolve("🎉 Success promise!"); // Успешное выполнение

  return Promise.reject("💢 Error promise!") // Завершение с ошибкой
};

async function run() {
  // Ожидаем выполнения всех промисов и обрабатываем результаты с помощью Promise.allSettled.
  const data = await Promise.allSettled([
    testPromise(true),
    testPromise(false), // Завершится с ошибкой
    testPromise(true),
  ]);

  // Находим первый выполненный промис и извлекаем его значение.
  const response = data.find(isPromiseFulfilled)?.value;
  console.log("Ответ успешного промиса:", response);

  // Находим первый отклонённый промис и извлекаем причину ошибки.
  const error = data.find(isPromiseRejected)?.reason;
  if (error) {
    console.error("Ошибка отклонённого промиса:", error);
  }

  // Проверяем, что все промисы успешно выполнены.
  const allFulfilled = data.every(isPromiseFulfilled);
  if (allFulfilled) {
    console.log("Все промисы успешно выполнены!");

    // Получаем массив всех успешных значений.
    const results = data.filter(isPromiseFulfilled).map(result => result.value);
    console.log("Результаты всех промисов:", results);
  } else {
    console.warn("Некоторые промисы завершились с ошибкой.");
  }
}

run();

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

Зачем использовать Promise.allSettled?

  • Обработка независимых задач: Когда вам нужно выполнить несколько независимых асинхронных операций, и важно получить информацию о результате каждой из них, даже если некоторые операции завершатся с ошибкой. Например, загрузка данных из нескольких источников, где неудача одной загрузки не должна блокировать обработку результатов других.
  • Улучшение обработки ошибок: Promise.allSettled позволяет обрабатывать ошибки отдельных промисов без прерывания всего процесса. Вы получаете информацию о причинах ошибок, что упрощает отладку и обработку исключительных ситуаций.
  • Более полная картина: Вы получаете полную информацию о состоянии всех промисов, что полезно для мониторинга и логирования.