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

Ответ

При разработке и тестировании асинхронного кода на 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 за одним ресурсом).

Ответ 18+ 🔞

Э, сабака сука, давай разберём эти промисы, а то голова уже пухнет! Сидишь такой, пишешь код, а тут на тебе — овердохуища способов их запулить параллельно. Главное, не перепутать, а то будет тебе хиросима, а не асинхронность.

Смотри, есть у нас два главных героя: Promise.all и Promise.any. Похожи только названием, а ведут себя — хуй с горы, совсем по-разному. Один — перфекционист ебаный, другой — хитрая жопа, которой лишь бы первый успешный.

Promise.all(iterable) — командир отряда

Логика: Этот чувак ждёт, блядь, пока ВСЕ промисы в массиве не отрапортуют об успехе. Как будто построил солдат и командует: «Не сдвинемся с места, пока последний не доложит!».

  • Если все молодцы: Возвращает массив результатов в том же порядке, в каком промисы стояли. Красота, порядок.
  • Если хоть один обосрался: Всё, пиздец. Он сразу падает с ошибкой того самого первого, кто не справился. Остальные-то продолжают работать, но их результаты уже всем похуй, их игнорируют.
  • На пальцах: Представь, собираешь комп. Заказал процессор, видяху и мать. Promise.all — это когда тебе привозят всю посылку разом. Но если видяха приехала битая, то хоть остальное и целое, но весь заказ — брак. Иди разбирайся с первым косяком.
// Допустим, грузим два конфига, без них приложение — ни хуя себе.
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) {
        // Сюда влетим, если хоть один файлик не найдётся (404) или сеть сдохнет.
        console.error('Конфигурацию не собрать, пизда рулю:', error);
        showErrorScreen();
    }
}

Promise.any(iterable) — хитрая жопа

Логика: А этот уже не такой принципиальный. Ему нужен ПЕРВЫЙ УСПЕШНЫЙ промис. Как будто кинул удочки в три озера — какая первая клюнет, ту и тащи.

  • Если хоть один выстрелил: Возвращает значение этого счастливчика. Остальные можно хоть на хуй послать, их результаты не важны.
  • Если все как один обосрались: Вот тогда он охуевает и падает. Но падает с приколом — выдаёт AggregateError, куда запихивает массив всех ошибок. Чтобы ты знал, насколько всё плохо.
  • На пальцах: Ищешь, где скачать фильм. Запускаешь поиск по пяти сайтам-зеркалам. Promise.any — это когда тебе нужна первая же рабочая ссылка, с которой начнётся загрузка. Похуй, что на остальных четырёх лежит вирусняк или они не грузятся. Провал будет только если все пять — говно.
// Грузим данные: сначала с основного API, если лагает — с запасного, ну а в крайнем случае из кеша.
const primaryDataSource = fetchFromPrimaryAPI().catch(() => { throw new Error('Основной API сдох'); });
const secondaryDataSource = fetchFromSecondaryAPI().catch(() => { throw new Error('Запасной API тоже'); });
const fallbackDataSource = fetchFromCache().catch(() => { throw new Error('И кеш пустой, ядрёна вошь'); });

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('Сервис в полной жопе');
    }
}

Короче, табличка, чтобы не ебать себе мозг

Метод Чего ждёт? Что вернёт, если повезло Что вернёт, если обосралось Когда юзать?
Promise.all() Пока ВСЕ не сделают Массив побед в том же порядке Ошибку первого лузера Когда нужны результаты всех операций (сборка всего, без чего нельзя).
Promise.any() Пока ПЕРВЫЙ не выиграет Результат первого счастливчика AggregateError (если все проебались) Когда нужен любой успех из списка (резервные пути, самый быстрый ответ).

Итог, чувак:

  • Суешься в Promise.all(), когда тебе каждый результат важен, как воздух. Без всех вместе — нихуя не работает.
  • Тыкаешь в Promise.any(), когда тебе хоть что-то да нужно, а откуда — похуй. Лишь бы хоть один справился.