Продолжит ли работу Promise.all если один из промисов завершится с ошибкой

«Продолжит ли работу Promise.all если один из промисов завершится с ошибкой» — вопрос из категории Асинхронность, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Пример из реального проекта:

async function fetchMultipleAPIs(endpoints) {
  const promises = endpoints.map(endpoint => 
    fetch(endpoint)
      .then(response => {
        if (!response.ok) {
          throw new Error(`HTTP ${response.status} for ${endpoint}`);
        }
        return response.json();
      })
      .catch(error => {
        // Логируем, но прокидываем ошибку дальше
        console.error(`Failed to fetch ${endpoint}:`, error.message);
        throw error; // Важно: пробрасываем ошибку
      })
  );

  try {
    const results = await Promise.all(promises);
    console.log('All APIs fetched successfully');
    return results;
  } catch (error) {
    // Сюда попадем при первой же ошибке в любом промиссе
    console.error('Promise.all failed early:', error.message);

    // В реальном проекте здесь была бы логика fallback
    const fallbackData = await loadCachedData();
    return fallbackData;
  }
}

// Использование
const endpoints = [
  'https://api.example.com/users',
  'https://api.example.com/products',
  'https://api.example.com/invalid', // Этот упадет
  'https://api.example.com/orders',
];

// Третий запрос упадет -> весь Promise.all упадет
// Четвертый запрос даже не дождется своего результата

Альтернативы, которые я использовал:

1. Promise.allSettled — когда нужны все результаты:

const results = await Promise.allSettled(promises);

const successful = results
  .filter(r => r.status === 'fulfilled')
  .map(r => r.value);

const failed = results
  .filter(r => r.status === 'rejected')
  .map(r => r.reason);

console.log(`Success: ${successful.length}, Failed: ${failed.length}`);

2. Индивидуальная обработка — для критичных операций:

async function fetchWithIndividualHandling(endpoints) {
  const results = [];
  const errors = [];

  for (const endpoint of endpoints) {
    try {
      const data = await fetch(endpoint).then(r => r.json());
      results.push(data);
    } catch (error) {
      errors.push({ endpoint, error: error.message });
      // Продолжаем выполнение остальных
      continue;
    }
  }

  return { results, errors };
}

3. Promise.all с перехватом ошибок — обертка для каждого промиса:

const safePromises = endpoints.map(endpoint =>
  fetch(endpoint)
    .then(r => r.json())
    .catch(error => ({
      error: true,
      endpoint,
      message: error.message
    }))
);

const results = await Promise.all(safePromises);
// Все промисы завершатся, даже с ошибками

Когда что использовать:

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