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

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

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

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

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

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

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

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

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

Примеры

Выполнение всех промисов:

const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});
 
promiseAll([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]
});

Отклонение одного из промисов:

const p1 = Promise.resolve(1);
const p2 = Promise.reject('error');
const p3 = Promise.resolve(3);
 
promiseAll([p1, p2, p3]).catch(error => {
  console.error(error); // "error"
});

Требования

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

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

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

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

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

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