В чем разница между Promise.all() и Promise.any() в JavaScript?

«В чем разница между Promise.all() и Promise.any() в JavaScript?» — вопрос из категории JavaScript, который задают на 24% собеседований AQA / Automation. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

При разработке и тестировании асинхронного кода на JavaScript я использую эти методы для разных сценариев параллельного выполнения промисов. Их поведение при успехе и ошибке кардинально различается.

Promise.all(iterable)

  • Логика: Ожидает успешного завершения ВСЕХ промисов в переданном массиве (или итерируемом объекте).
  • Успех: Возвращает массив результатов в том же порядке, что и исходные промисы.
  • Неудача: Немедленно отклоняется с причиной (error) первого отклоненного промиса. Остальные промисы продолжают выполняться, но их результаты игнорируются.
  • Аналогия: Групповой заказ: еда подается только когда готовы все блюда. Если одно блюдо испорчено (отклонен промис), весь заказ отменяется.
// Пример: параллельная загрузка нескольких конфигурационных файлов
const loadUserConfig = fetch('/api/config/user.json').then(r => r.json());
const loadAppConfig = fetch('/api/config/app.json').then(r => r.json());

async function initializeApp() {
    try {
        // Оба конфига необходимы для старта приложения
        const [userConfig, appConfig] = await Promise.all([loadUserConfig, loadAppConfig]);
        console.log('Оба конфига загружены:', userConfig, appConfig);
        startApp(userConfig, appConfig);
    } catch (error) {
        // Если хотя бы один fetch упадет с 404, мы попадем сюда
        console.error('Не удалось загрузить конфигурацию:', error);
        showErrorScreen();
    }
}

Promise.any(iterable)

  • Логика: Ожидает успешного завершения ПЕРВОГО успешного промиса.
  • Успех: Возвращает значение первого успешно выполнившегося промиса.
  • Неудача: Отклоняется только если отклонены ВСЕ промисы в переданном массиве. Возвращает объект AggregateError, содержащий массив всех ошибок.
  • Аналогия: Поиск запчасти: обзваниваем несколько поставщиков. Нам нужна деталь от того, кто ответит первым и скажет "есть в наличии". Ошибка ("нет в наличии") нас не пугает, пока есть другие поставщики. Провал наступает только если все сказали "нет".
// Пример: резервирование источников данных (primary, secondary, fallback)
const primaryDataSource = fetchFromPrimaryAPI().catch(() => { throw new Error('Primary failed'); });
const secondaryDataSource = fetchFromSecondaryAPI().catch(() => { throw new Error('Secondary failed'); });
const fallbackDataSource = fetchFromCache().catch(() => { throw new Error('Cache failed'); });

async function getCriticalData() {
    try {
        // Нам нужны данные хотя бы из одного источника
        const data = await Promise.any([primaryDataSource, secondaryDataSource, fallbackDataSource]);
        console.log('Данные получены из первого доступного источника:', data);
        return data;
    } catch (aggregateError) {
        // Сюда попадем, только если ВСЕ три источника отказали
        console.error('Все источники данных недоступны:', aggregateError.errors);
        throw new Error('Service unavailable');
    }
}

Сравнительная таблица

Метод Ждет... Возвращает при успехе Возвращает при ошибке Типичный use-case
Promise.all() Всех Массив результатов Ошибку первого отклоненного Параллельная загрузка взаимозависимых данных.
Promise.any() Первого успешного Результат первого успешного AggregateError (если все упали) Резервирование, выбор самого быстрого источника.

Выбор инструмента:

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