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

Какие знаешь утилиты Promise кроме Promise.all?

1.6 Junior🔥 161 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

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

# Утилиты Promise кроме Promise.all

В JavaScript есть много встроенных и полезных утилит для работы с Promise. Рассмотрим основные и их применение.

Встроенные утилиты Promise

1. Promise.race()

Возвращает результат первого решённого Promise:

// Пример 1: Таймаут запроса
function fetchWithTimeout(url, timeout = 5000) {
  return Promise.race([
    fetch(url),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), timeout)
    ),
  ]);
}

fetchWithTimeout('https://api.example.com/data')
  .then(response => response.json())
  .catch(error => console.log(error.message)); // Timeout после 5 сек

// Пример 2: Первый ответивший сервер
const servers = [
  'https://api1.example.com',
  'https://api2.example.com',
  'https://api3.example.com',
];

const firstResponse = Promise.race(
  servers.map(server => fetch(server))
);

firstResponse.then(response => {
  console.log('Got response from fastest server');
});

// Пример 3: Отмена операции по timeout
const operation = Promise.race([
  doLongOperation(),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Cancelled')), 10000)
  ),
]);

2. Promise.allSettled()

Ожидает все Promise и возвращает результаты (успех или ошибка):

// Promise.allSettled НЕ отклоняется, даже если один Promise отклонится
const promises = [
  Promise.resolve(1),
  Promise.reject(new Error('Error')),
  Promise.resolve(3),
];

Promise.allSettled(promises).then(results => {
  console.log(results);
  // [
  //   { status: 'fulfilled', value: 1 },
  //   { status: 'rejected', reason: Error('Error') },
  //   { status: 'fulfilled', value: 3 }
  // ]
});

// Практический пример: загрузка нескольких ресурсов
const userIds = [1, 2, 3, 4, 5];

const promises = userIds.map(id =>
  fetch(`/api/users/${id}`)
    .then(r => r.json())
    .catch(e => ({ error: e.message, userId: id }))
);

Promise.allSettled(promises).then(results => {
  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('Loaded:', successful.length);
  console.log('Failed:', failed.length);
});

3. Promise.any()

Возвращает первый успешно решённый Promise. Если все отклонены, выбрасывает ошибку:

// Получаем первый успешный запрос
const servers = [
  'https://api1.example.com/data',
  'https://api2.example.com/data',
  'https://api3.example.com/data',
];

Promise.any(servers.map(url => fetch(url)))
  .then(response => response.json())
  .then(data => console.log('Got data:', data))
  .catch(error => console.log('All servers failed:', error));

// Практический пример: подключение к нескольким сервисам
const connectToService = (service) => {
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(
      () => reject(new Error(`${service} timeout`)),
      2000
    );
    
    performConnection(service)
      .then(result => {
        clearTimeout(timeout);
        resolve(result);
      })
      .catch(error => {
        clearTimeout(timeout);
        reject(error);
      });
  });
};

Promise.any([
  connectToService('redis'),
  connectToService('memcache'),
  connectToService('mongodb'),
])
  .then(connection => console.log('Connected'))
  .catch(error => console.log('All services down:', error));

Встроенные методы Promise

4. Promise.resolve()

Создаёт уже решённый Promise:

// Обычное использование
Promise.resolve(42).then(value => console.log(value)); // 42

// С thenable объектом
const thenable = {
  then(resolve) {
    resolve('Custom thenable');
  },
};

Promise.resolve(thenable).then(value => console.log(value));

// Практический пример: унификация асинхронного кода
function getData(source) {
  if (source instanceof Promise) {
    return source;
  }
  if (typeof source === 'function') {
    return Promise.resolve(source());
  }
  return Promise.resolve(source);
}

getData(42).then(console.log);                      // 42
getData(() => 42).then(console.log);                // 42
getData(Promise.resolve(42)).then(console.log);     // 42

5. Promise.reject()

Создаёт отклонённый Promise:

Promise.reject(new Error('Something went wrong'))
  .catch(error => console.log(error.message));

// Практический пример: валидация
function validateUser(user) {
  if (!user.email) {
    return Promise.reject(new Error('Email is required'));
  }
  return Promise.resolve(user);
}

validateUser({}).catch(error => console.log(error.message));

Полезные паттерны и комбинации

Пример 1: Promise.all с обработкой ошибок

// ❌ Promise.all отклоняется при первой ошибке
Promise.all([
  fetch('/api/users'),
  fetch('/api/posts'),
  fetch('/api/comments'),
])
  .catch(error => console.log('One failed:', error));

// ✅ Promise.allSettled продолжает загружать
Promise.allSettled([
  fetch('/api/users'),
  fetch('/api/posts'),
  fetch('/api/comments'),
])
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Request ${index} succeeded`);
      } else {
        console.log(`Request ${index} failed:`, result.reason);
      }
    });
  });

Пример 2: Композиция Promise

// Запустить параллельно, потом последовательно
Promise.all([
  fetch('/api/user'),
  fetch('/api/settings'),
])
  .then(responses => Promise.all(responses.map(r => r.json())))
  .then(([user, settings]) => {
    console.log('User:', user);
    console.log('Settings:', settings);
  });

Пример 3: Retry с Promise.race

function retryWithTimeout(fn, retries = 3, timeout = 5000) {
  const attempt = () => {
    return Promise.race([
      fn(),
      new Promise((_, reject) =>
        setTimeout(() => reject(new Error('Timeout')), timeout)
      ),
    ]);
  };
  
  return attempt().catch(error => {
    if (retries <= 0) throw error;
    return retryWithTimeout(fn, retries - 1, timeout);
  });
}

retryWithTimeout(
  () => fetch('/api/unstable'),
  3,
  5000
).then(response => response.json());

Пример 4: Чейн с обработкой ошибок

fetch('/api/user')
  .then(r => r.json())
  .then(user => fetch(`/api/user/${user.id}/posts`))
  .then(r => r.json())
  .then(posts => {
    console.log('User posts:', posts);
  })
  .catch(error => {
    // Ловит ошибку из любого шага
    console.log('Error:', error);
  });

Таблица сравнения

МетодВозвращаетПри ошибкеКогда использовать
all()Массив результатовОтклоняетсяВсе успешные
allSettled()Массив результатов/ошибокНикогдаВсе равно результаты
race()Первый результатОтклоняетсяПервый готов
any()Первый успешныйОтклоняетсяПервый успешный
resolve()Решённый Promise-Унификация
reject()Отклонённый Promise-Явная ошибка

Практические советы

  1. Используй Promise.allSettled() для независимых операций, где ошибка в одной не блокирует остальные

  2. Используй Promise.race() для таймаутов — это идеальное применение

  3. Используй Promise.any() для redundancy — когда нужна хотя бы одна успешная операция

  4. Комбинируй методы — Promise.all + Promise.allSettled часто используются вместе

  5. Помни про обработку ошибок — всегда добавляй .catch() в цепь промисов