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

Как выполнить все асинхронные запросы?

1.8 Middle🔥 211 комментариев
#Node.js и JavaScript#Фреймворки и библиотеки

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

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

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

Как выполнить все асинхронные запросы

Выполнение нескольких асинхронных операций параллельно — это одна из ключевых задач в Node.js. Существует несколько подходов, каждый с собственными преимуществами и недостатками.

1. Promise.all() — выполнить все, ошибка = отказ

Это наиболее распространённый способ. Все промисы выполняются параллельно, и результат возвращается массивом в том же порядке.

const fetchUser = () => new Promise(resolve => 
  setTimeout(() => resolve({ id: 1, name: 'John' }), 100)
);

const fetchPosts = () => new Promise(resolve => 
  setTimeout(() => resolve([{ id: 1, title: 'Post 1' }]), 150)
);

const fetchComments = () => new Promise(resolve => 
  setTimeout(() => resolve([{ id: 1, text: 'Comment 1' }]), 200)
);

// Выполнить все параллельно
Promise.all([
  fetchUser(),
  fetchPosts(),
  fetchComments()
]).then(([user, posts, comments]) => {
  console.log('User:', user);
  console.log('Posts:', posts);
  console.log('Comments:', comments);
}).catch(error => {
  console.error('Ошибка:', error);
});

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

2. Promise.allSettled() — выполнить все, независимо от ошибок

Этот метод ждёт завершения всех промисов, включая отклонённые. Результат — массив объектов { status, value/reason }.

const requests = [
  fetch('/api/user').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/invalid').then(r => r.json()) // может быть ошибка
];

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

// Результат:
// Запрос 0 успешен: { id: 1, ... }
// Запрос 1 успешен: [ { id: 1, ... } ]
// Запрос 2 ошибка: Error: 404

3. Promise.race() — первый результат

Выполняет все промисы параллельно, но возвращает результат первого завершённого промиса (успешного или ошибки).

const fetchWithTimeout = (url, timeout = 5000) => {
  return Promise.race([
    fetch(url).then(r => r.json()),
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('Timeout')), timeout)
    )
  ]);
};

fetchWithTimeout('/api/user', 3000)
  .then(data => console.log('Данные:', data))
  .catch(error => console.error('Ошибка:', error));

4. async/await с Promise.all()

Модный синтаксис, более читаемый чем then/catch:

async function getAllData() {
  try {
    const [user, posts, comments] = await Promise.all([
      fetch('/api/user').then(r => r.json()),
      fetch('/api/posts').then(r => r.json()),
      fetch('/api/comments').then(r => r.json())
    ]);
    
    console.log('User:', user);
    console.log('Posts:', posts);
    console.log('Comments:', comments);
    
    return { user, posts, comments };
  } catch (error) {
    console.error('Ошибка при получении данных:', error);
    throw error;
  }
}

// Использование
getAllData().then(result => console.log('Все готово:', result));

5. Чередование параллельных и последовательных операций

Иногда нужно выполнить операции частично параллельно, частично последовательно:

async function complexFlow() {
  // 1. Сначала получить пользователя (он нужен для следующих запросов)
  const user = await fetch('/api/user').then(r => r.json());
  console.log('Получен пользователь:', user);
  
  // 2. Затем параллельно получить посты и комментарии
  const [posts, comments] = await Promise.all([
    fetch(`/api/user/${user.id}/posts`).then(r => r.json()),
    fetch(`/api/user/${user.id}/comments`).then(r => r.json())
  ]);
  
  console.log('Посты:', posts);
  console.log('Комментарии:', comments);
  
  return { user, posts, comments };
}

6. Обработка ошибок с Promise.allSettled()

Для надёжной обработки нескольких независимых запросов:

async function robustFetch() {
  const results = await Promise.allSettled([
    fetch('/api/user').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/stats').then(r => r.json())
  ]);
  
  const successfulResults = results
    .filter(r => r.status === 'fulfilled')
    .map(r => r.value);
  
  const failedResults = results
    .filter(r => r.status === 'rejected')
    .map((r, i) => ({ index: i, error: r.reason }));
  
  console.log('Успешно:', successfulResults);
  console.log('Ошибки:', failedResults);
  
  return { success: successfulResults, errors: failedResults };
}

7. Обработка очереди асинхронных операций

Для выполнения множества операций с контролем параллелизма:

async function processWithLimit(tasks, limit = 3) {
  const results = [];
  const executing = [];
  
  for (const task of tasks) {
    const promise = Promise.resolve().then(() => task()).then(result => {
      executing.splice(executing.indexOf(promise), 1);
      return result;
    });
    
    results.push(promise);
    executing.push(promise);
    
    if (executing.length >= limit) {
      await Promise.race(executing);
    }
  }
  
  return Promise.all(results);
}

// Использование
const tasks = [
  () => fetch('/api/1').then(r => r.json()),
  () => fetch('/api/2').then(r => r.json()),
  () => fetch('/api/3').then(r => r.json()),
  () => fetch('/api/4').then(r => r.json()),
  () => fetch('/api/5').then(r => r.json())
];

processWithLimit(tasks, 2).then(results => {
  console.log('Все результаты:', results);
});

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

МетодПоведениеКогда использовать
Promise.all()Все выполнены или ошибкаЗависимые данные, нужны все результаты
Promise.allSettled()Все выполнены, результаты независимо от ошибокНезависимые запросы, нужны все результаты
Promise.race()Первый результатTimeouts, fallbacks
async/awaitСинтаксический сахар для Promise.allЧитаемость, простые случаи
Ограничение параллелизмаN одновременных операцийБольшие батчи, ограничение ресурсов

Практический пример для Node.js Backend

const express = require('express');
const app = express();

app.get('/api/dashboard', async (req, res) => {
  try {
    // Параллельно получаем данные из БД и внешних API
    const [userData, analytics, settings] = await Promise.all([
      getUserFromDB(req.user.id),
      getAnalyticsFromAPI(),
      getSettingsFromCache()
    ]);
    
    res.json({
      user: userData,
      analytics: analytics,
      settings: settings
    });
  } catch (error) {
    console.error('Dashboard error:', error);
    res.status(500).json({ error: 'Failed to load dashboard' });
  }
});

app.listen(3000);

Заключение

Для выполнения всех асинхронных запросов:

  1. Promise.all() — когда нужны все результаты и ошибка критична
  2. Promise.allSettled() — когда нужны все результаты, но ошибки не критичны
  3. async/await — для читаемости и простоты
  4. Ограничение параллелизма — при большом количестве операций

Выбор зависит от требований: если один отказ — критичен, используйте all(). Если независимые запросы и нужны все результаты — allSettled().