🟨 JavaScript
Сложная
🕐 15 мин

Реализация Promise.allSettled()

Цель: понять разницу между Promise.all() и Promise.allSettled() и попрактиковаться в обработке всех исходов промисов.

💡 Подсказка к решению
  1. Как и Promise.all, ваша функция promiseAllSettled должна возвращать новый Promise.
  2. Ключевое отличие: Promise.allSettled никогда не отклоняется. Он ждет завершения всех промисов.
  3. Вам также понадобится массив results и счетчик settledCount.
  4. Перебирайте массив promises. Для каждого элемента:
    • Используйте Promise.resolve() для универсальной обработки.
    • В then() добавляйте в results объект вида { status: 'fulfilled', value: ... }.
    • В catch() добавляйте в results объект вида { status: 'rejected', reason: ... }.
    • И в then, и в catch увеличивайте счетчик settledCount.
    • Когда settledCount станет равен длине массива promises, вызовите resolve() с массивом results.
  5. Не забудьте про случай с пустым массивом.
👀 Решение
const promiseAllSettled = (promises) => {
  // Возвращаем новый промис. Он будет разрешен, когда все промисы в `promises` завершатся.
  // Важно, что этот промис никогда не будет отклонен, в отличие от Promise.all.
  return new Promise((resolve) => {
    // Массив для хранения итоговых состояний всех промисов.
    const results = [];
    // Счетчик для отслеживания количества завершенных (settled) промисов.
    let settledCount = 0;
    // Общее количество промисов для удобства.
    const promisesCount = promises.length;
 
    // Если входной массив пуст, немедленно разрешаем промис с пустым массивом.
    if (promisesCount === 0) {
      resolve([]);
      return;
    }
 
    // Перебираем каждый элемент входного массива.
    promises.forEach((promise, index) => {
      // Оборачиваем каждый элемент в `Promise.resolve()`, чтобы единообразно
      // обрабатывать и промисы, и обычные значения.
      Promise.resolve(promise)
        .then(value => {
          // Если промис успешно выполнен (fulfilled),
          // сохраняем его результат в виде объекта со статусом 'fulfilled'.
          results[index] = { status: 'fulfilled', value };
        })
        .catch(reason => {
          // Если промис отклонен (rejected),
          // сохраняем причину отклонения в виде объекта со статусом 'rejected'.
          results[index] = { status: 'rejected', reason };
        })
        .finally(() => {
          // Блок `finally` выполняется в любом случае: и после `then`, и после `catch`.
          // Это гарантирует, что счетчик увеличится для каждого промиса, независимо от его исхода.
          settledCount++;
          // Если количество завершенных промисов равно общему количеству,
          // значит, все промисы завершились.
          if (settledCount === promisesCount) {
            // Разрешаем основной промис, возвращая массив с результатами.
            resolve(results);
          }
        });
    });
  });
};

Анализ решения:

  • Сложность по времени: O(N), где N — количество промисов, плюс время выполнения самого долгого промиса.
  • Сложность по памяти: O(N) для хранения результатов.

Описание задачи

Вам нужно реализовать свою собственную версию встроенного статического метода Promise.allSettled().

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

Для каждого результата есть:

  • status (строка). Либо 'fulfilled', либо 'rejected'.
  • Если статус 'fulfilled', то присутствует value.
  • Если статус 'rejected', то присутствует reason.

Примеры

const p1 = Promise.resolve(3);
const p2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
 
promiseAllSettled([p1, p2]).then(results => {
  /*
  results будет:
  [
    { status: 'fulfilled', value: 3 },
    { status: 'rejected',  reason: 'foo' }
  ]
  */
  console.log(results);
});

Требования

  • Функция должна называться promiseAllSettled.
  • Она должна принимать итерируемый объект (например, массив).
  • Она должна возвращать один Promise.
  • Возвращенный промис должен выполниться с массивом объектов результатов, когда все промисы в итерируемом объекте завершены.
  • Порядок результатов должен соответствовать порядку промисов.
  • Возвращенный промис никогда не должен отклоняться.

🧑‍💻 Это не баг! Это фича!

Редактор кода намеренно скрыт на мобильном.

Поверь, так лучше: я оберегаю тебя от искушения писать код в неидеальных условиях. Маленький экран и виртуальная клавиатура — не лучшие помощники для программиста.

📖 Сейчас: Изучи задачу, продумай решение. Действуй как стратег.

💻 Потом: Сядь за компьютер, открой сайт и реализуй все идеи с комфортом. Действуй как код-джедай!