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

Как обработать сразу несколько Promise, если один из них был отклонен?

2.2 Middle🔥 261 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Обработка нескольких Promise с отклоненными значениями

Работа с несколькими асинхронными операциями - частая задача в JavaScript. Существует несколько методов для обработки группы Promise, когда один из них может быть отклонен.

1. Promise.all() - все успешны или один отклонен

Promise.all() выполняет все Promise и возвращает результат, если все успешны, или отклонение при первой ошибке.

const promises = [
  fetch('/api/users').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/comments').then(r => r.json())
]

Promise.all(promises)
  .then(([users, posts, comments]) => {
    console.log('Все данные загружены:', users, posts, comments)
  })
  .catch(error => {
    console.error('Ошибка при загрузке данных:', error)
  })

Проблема: если один Promise отклоняется, все остальные игнорируются (даже если уже выполнены).

2. Promise.allSettled() - гарантирует получить результаты всех

Promise.allSettled() - современный подход. Ждёт выполнения всех Promise, независимо от успеха, и возвращает массив объектов со статусом.

const promises = [
  fetch('/api/users').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/comments').then(r => r.json())
]

Promise.allSettled(promises)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Запрос ${index} успешен:`, result.value)
      } else {
        console.error(`Запрос ${index} провалился:`, result.reason)
      }
    })
  })

Формат результата:

[
  { status: 'fulfilled', value: {...} },
  { status: 'rejected', reason: Error(...) },
  { status: 'fulfilled', value: {...} }
]

3. Promise.race() - первый результат (успех или ошибка)

Promise.race() возвращает результат первого завершённого Promise.

const promises = [
  fetch('/api/fast-endpoint').then(r => r.json()),
  new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Timeout')), 5000)
  )
]

Promise.race(promises)
  .then(data => console.log('Первый результат:', data))
  .catch(error => console.error('Ошибка или timeout:', error))

Этот метод полезен для реализации таймаутов.

4. Promise.any() - первый успешный

Promise.any() ждёт первого успешного Promise. Если все отклоняются, возвращает AggregateError.

const mirrors = [
  fetch('https://mirror1.com/file'),
  fetch('https://mirror2.com/file'),
  fetch('https://mirror3.com/file')
]

Promise.any(mirrors)
  .then(response => console.log('Загружено с:', response.url))
  .catch(error => {
    console.error('Все зеркала недоступны')
    console.error('Причины:', error.errors)
  })

5. Обработка с try/catch и async/await

Модерный способ с условной обработкой ошибок:

async function loadAllData() {
  const results = await Promise.allSettled([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/comments').then(r => r.json())
  ])

  const data = {}
  const errors = {}

  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      data[index] = result.value
    } else {
      errors[index] = result.reason.message
    }
  })

  return { data, errors }
}

try {
  const { data, errors } = await loadAllData()
  if (Object.keys(errors).length > 0) {
    console.warn('Некоторые запросы провалились:', errors)
  }
  console.log('Успешные данные:', data)
} catch (error) {
  console.error('Критическая ошибка:', error)
}

6. Обработка с условной логикой

Иногда нужно разные сценарии для разных ошибок:

function loadDataWithFallbacks() {
  return Promise.allSettled([
    fetch('/api/primary').then(r => r.json()),
    fetch('/api/backup').then(r => r.json())
  ])
    .then(results => {
      const primary = results[0]
      const backup = results[1]

      // Если основной успешен - используем его
      if (primary.status === 'fulfilled') {
        return primary.value
      }

      // Если основной провалился, используем резервный
      if (backup.status === 'fulfilled') {
        console.warn('Основной источник недоступен, используем резервный')
        return backup.value
      }

      // Если оба провалились - выбрасываем ошибку
      throw new Error('Оба источника данных недоступны')
    })
}

7. Обработка с timeout

Часто нужно отменить Promise если он выполняется слишком долго:

function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), ms)
  )
  return Promise.race([promise, timeout])
}

async function loadWithTimeout() {
  try {
    const data = await withTimeout(
      fetch('/api/slow-endpoint').then(r => r.json()),
      5000 // 5 секунд
    )
    console.log('Данные загружены:', data)
  } catch (error) {
    console.error('Ошибка или timeout:', error.message)
  }
}

8. Использование AbortController для отмены

Для более точного контроля используйте AbortController:

const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 5000)

fetch('/api/data', { signal: controller.signal })
  .then(r => r.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.error('Запрос отменён по timeout')
    } else {
      console.error('Ошибка запроса:', error)
    }
  })
  .finally(() => clearTimeout(timeoutId))

Сравнение методов

МетодВозвращаетПри ошибкеИспользование
Promise.all()Массив результатовОтклонение на первой ошибкеВсе должны успеть
Promise.allSettled()Массив объектов {status, value/reason}Не выбрасываетЧастичные данные OK
Promise.race()Первый результатПервая ошибкаТаймауты, гонки
Promise.any()Первый успешныйAggregateError если все ошибкиЗеркала, fallbacks

Рекомендации

  • Promise.allSettled() - лучший выбор большинства случаев, когда нужны все данные
  • Promise.all() - только если все Promise критичны и ошибка одного блокирует всё
  • Promise.any() - для fallback сценариев (несколько источников данных)
  • Promise.race() - для таймаутов и гонок
  • async/await + try/catch - самый читаемый и современный синтаксис
  • Всегда обрабатывайте ошибки явно - это критично для UX

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

Как обработать сразу несколько Promise, если один из них был отклонен? | PrepBro