Gernar
JavaScript: асинхронность

Что такое Promise.all

Разбор вопроса «Что такое Promise.all» для Frontend Developer: что проверяет интервьюер, ключевые тезисы, практические примеры и частые ошибки.

Вопрос

Что такое Promise.all

Профессия

Frontend Developer

Что хочет услышать интервьюер

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

Ключевые тезисы

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

Подробный ответ

Promise.all — это статический метод в JavaScript, который позволяет выполнять несколько асинхронных операций параллельно и дожидаться их завершения. Он принимает массив (или итерируемый объект) промисов и возвращает новый промис. Этот новый промис разрешится только тогда, когда все переданные промисы успешно завершатся. Результатом будет массив значений в том же порядке, что и исходные промисы. Если хотя бы один промис будет отклонен (rejected), Promise.all немедленно вернет ошибку, не дожидаясь завершения остальных промисов. Это делает его полезным для сценариев, где необходимо выполнить несколько независимых асинхронных операций и обработать их результаты вместе.

Одним из ключевых аспектов Promise.all является его поведение при ошибках. Если один из промисов завершается с ошибкой, весь Promise.all считается отклоненным, и остальные промисы игнорируются. Это может быть проблемой, если нужно обработать все результаты, включая ошибки. В таких случаях можно использовать методы, такие как Promise.allSettled, который ждет завершения всех промисов, независимо от их статуса.

Promise.all также поддерживает передачу не-промисов в массиве. Если элемент массива не является промисом, он будет преобразован в разрешенный промис с этим значением. Это делает метод гибким и удобным для работы с混合ными данными.

Важно отметить, что Promise.all выполняет промисы параллельно, но порядок результатов сохраняется согласно порядку исходных промисов. Это гарантирует, что результаты будут соответствовать ожидаемому порядку, даже если некоторые промисы завершатся быстрее других.

Практические примеры

Пример 1

Загрузка нескольких файлов параллельно

const fileUrls = ['file1.txt', 'file2.txt', 'file3.txt'];
const filePromises = fileUrls.map(url => fetch(url).then(response => response.text()));

Promise.all(filePromises)
  .then(contents => {
    console.log('Все файлы загружены:', contents);
  })
  .catch(error => {
    console.error('Ошибка загрузки файла:', error);
  });

Пример 2

Обработка ошибок с Promise.allSettled

const promises = [
  Promise.resolve('Успех 1'),
  Promise.reject('Ошибка 1'),
  Promise.resolve('Успех 2')
];

Promise.allSettled(promises)
  .then(results => {
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        console.log('Успех:', result.value);
      } else {
        console.log('Ошибка:', result.reason);
      }
    });
  });

Пример 3

Передача не-промисов в Promise.all

Promise.all([1, 2, Promise.resolve(3), Promise.resolve(4)])
  .then(values => {
    console.log(values); // [1, 2, 3, 4]
  });

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

  • Игнорирование обработки ошибок в Promise.all. Кандидаты часто забывают добавлять .catch() для обработки возможных ошибок, что может привести к неожиданным сбоям.
  • Предположение, что Promise.all сохраняет порядок выполнения промисов. На самом деле, порядок результатов соответствует порядку исходных промисов, но выполнение происходит параллельно.

Связанные темы

  • Promise.race — метод, который возвращает первый завершенный промис (успешный или отклоненный).
  • Promise.allSettled — метод, который ждет завершения всех промисов, независимо от их статуса.
  • Async/Await — современный способ работы с асинхронным кодом, который может быть использован вместе с Promise.all.

Follow-up вопросы

Что произойдет, если один из промисов в Promise.all будет отклонен (rejected)?

Уровень: basic

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

Как можно обработать ошибки в Promise.all, чтобы не прерывать выполнение всех промисов?

Уровень: intermediate

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

Чем отличается Promise.all от Promise.race?

Уровень: intermediate

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

Можно ли передавать в Promise.all не только промисы, но и обычные значения?

Уровень: basic

Да, Promise.all автоматически оборачивает не-промисы в успешно завершенные промисы (Promise.resolve), сохраняя их значения в итоговом массиве.

Как реализовать аналог Promise.all с ограничением на количество одновременно выполняемых промисов (например, не более 3)?

Уровень: advanced

Для этого можно использовать библиотеки (например, p-limit) или написать кастомную функцию, которая запускает промисы порциями, отслеживая их завершение через рекурсию или цикл.

Содержание