Как выполнить все асинхронные запросы?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как выполнить все асинхронные запросы
Выполнение нескольких асинхронных операций параллельно — это одна из ключевых задач в 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);
Заключение
Для выполнения всех асинхронных запросов:
- Promise.all() — когда нужны все результаты и ошибка критична
- Promise.allSettled() — когда нужны все результаты, но ошибки не критичны
- async/await — для читаемости и простоты
- Ограничение параллелизма — при большом количестве операций
Выбор зависит от требований: если один отказ — критичен, используйте all(). Если независимые запросы и нужны все результаты — allSettled().