← Назад к вопросам

Чем воспользоваться для совершения действия после выполнения всех Promise?

2.0 Middle🔥 271 комментариев
#JavaScript Core

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Методы обработки завершения всех Promise

В JavaScript для выполнения действия после завершения всех Promise существует несколько ключевых подходов, выбор которых зависит от конкретной задачи. Вот основные из них, которые я использую в своей практике как Frontend Developer.

Promise.all()

Promise.all() — наиболее распространённый и строгий метод. Он принимает итерируемую коллекцию промисов (например, массив) и возвращает новый промис, который:

  • Исполняется (resolve) — когда все переданные промисы успешно завершаются. Результатом будет массив их результатов в том же порядке.
  • Отклоняется (reject) — при отклонении хотя бы одного промиса. Возвращается ошибка первого отклонённого промиса (остальные игнорируются, но продолжают выполняться).
const promise1 = fetch('/api/data1');
const promise2 = fetch('/api/data2');
const promise3 = fetch('/api/data3');

Promise.all([promise1, promise2, promise3])
    .then(([result1, result2, result3]) => {
        console.log('Все запросы успешны:', result1, result2, result3);
        // Действие после выполнения всех промисов
        renderDashboard([result1, result2, result3]);
    })
    .catch((error) => {
        console.error('Один из запросов завершился ошибкой:', error);
    });

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

Promise.allSettled() (ES2020)

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

const promises = [
    fetch('/api/users'),
    fetch('/api/posts'),
    fetch('/api/comments')
];

Promise.allSettled(promises)
    .then((results) => {
        const successfulData = [];
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`Промис ${index} успешен:`, result.value);
                successfulData.push(result.value);
            } else {
                console.warn(`Промис ${index} отклонён:`, result.reason);
            }
        });
        // Действие после завершения всех, с частичными результатами
        updateUI(successfulData);
    });

Используйте этот метод, когда нужно обработать все итоги и продолжить работу с доступными данными, например, при загрузке виджетов на дашборде, где неудача одного не должна блокировать остальные.

Promise.race() и Promise.any()

Хотя эти методы тоже ждут промисы, их поведение отличается:

  • Promise.race() — исполняется или отклоняется с результатом первого завершённого промиса (как успешного, так и ошибочного). Не подходит для ожидания всех.
  • Promise.any() (ES2021) — исполняется, когда хотя бы один промис успешно завершается, или отклоняется, если все промисы отклонены. Полезен для избыточных запросов к разным источникам, но не для ожидания всех.
// Пример Promise.any() — не для "всех", а для "первого успешного"
const backupRequests = [
    fetch('/primary-api/data').catch(() => {}),
    fetch('/secondary-api/data').catch(() => {})
];

Promise.any(backupRequests)
    .then((firstSuccess) => {
        console.log('Получены данные из первого доступного источника:', firstSuccess);
    });

Ручная агрегация с async/await

Для более сложных сценариев, например, с ограничением одновременных выполнений или динамическим списком промисов, можно использовать ручной подход с async/await:

async function handleAllPromises(promisesArray) {
    const results = [];
    const errors = [];

    // Используем цикл для последовательной или параллельной обработки
    for (const promise of promisesArray) {
        try {
            const result = await promise;
            results.push(result);
        } catch (error) {
            errors.push(error);
        }
    }

    console.log('Успешные результаты:', results);
    console.log('Ошибки:', errors);
    // Действие после всех промисов
    if (errors.length === 0) {
        return processResults(results);
    } else {
        throw new AggregateError(errors, 'Некоторые операции завершились с ошибкой');
    }
}

Ключевые рекомендации по выбору метода

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

В реальных проектах (например, в React-приложениях) я часто комбинирую эти методы с управлением состоянием (через Context, Redux или Zustand), чтобы корректно отражать этапы загрузки данных в интерфейсе. Главное — чётко определить бизнес-требования: нужно ли ждать абсолютно все операции или можно продолжить с частичными результатами.