Как получить результат трех параллельных асинхронных запросов?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как получить результат трех параллельных асинхронных запросов?
Получение результатов нескольких асинхронных операций параллельно — это частая задача. Есть несколько способов, и выбор зависит от твоих требований.
Способ 1: Promise.all() — простой и быстрый
Promise.all() выполняет все промисы параллельно и возвращает результаты в том же порядке.
// Три параллельных запроса
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json())
]);
console.log('Users:', users);
console.log('Posts:', posts);
console.log('Comments:', comments);
Практический пример:
async function loadDashboard() {
try {
const [userData, stats, notifications] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/stats').then(r => r.json()),
fetch('/api/notifications').then(r => r.json())
]);
return {
user: userData,
stats,
notifications
};
} catch (error) {
console.error('Failed to load dashboard:', error);
// Если любой из запросов упал — весь Promise.all() выбросит ошибку
}
}
const dashboard = await loadDashboard();
Преимущества:
- Выполняет запросы параллельно (быстро)
- Простая синтаксис
- Результаты в том же порядке
Недостатки:
- Если один запрос упал — остальные отменяются
- Может быть медленнее, если не все запросы важны
Способ 2: Promise.allSettled() — надежный
Promise.allSettled() выполняет все промисы и ждет, пока все завершатся, даже если некоторые упадут.
// Все три запроса выполнятся, даже если какие-то упадут
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())
]);
// results = [
// { status: 'fulfilled', value: [...] },
// { status: 'rejected', reason: Error },
// { status: 'fulfilled', value: [...] }
// ]
const [usersResult, postsResult, commentsResult] = results;
if (usersResult.status === 'fulfilled') {
console.log('Users loaded:', usersResult.value);
} else {
console.error('Failed to load users:', usersResult.reason);
}
if (postsResult.status === 'fulfilled') {
console.log('Posts loaded:', postsResult.value);
}
if (commentsResult.status === 'fulfilled') {
console.log('Comments loaded:', commentsResult.value);
}
Практический пример:
async function loadPageData() {
const results = await Promise.allSettled([
fetch('/api/profile').then(r => r.json()),
fetch('/api/recommendations').then(r => r.json()),
fetch('/api/analytics').then(r => r.json())
]);
const data = {};
// Profile — критично, нужно данные
if (results[0].status === 'fulfilled') {
data.profile = results[0].value;
} else {
throw new Error('Failed to load profile');
}
// Recommendations — не критично, может быть пусто
data.recommendations = results[1].status === 'fulfilled' ? results[1].value : [];
// Analytics — не критично
data.analytics = results[2].status === 'fulfilled' ? results[2].value : null;
return data;
}
Преимущества:
- Выполняет все запросы, даже если некоторые упадут
- Можно обработать каждый результат отдельно
- Подходит для случаев, когда не все данные критичны
Недостатки:
- Больше кода для обработки результатов
- Нужно проверять статус каждого результата
Способ 3: Promise.race() — первый результат
Promise.race() возвращает результат первого завершившегося промиса.
// Возвращает результат первого ответившего сервера
const firstResponse = await Promise.race([
fetch('https://api1.example.com/data').then(r => r.json()),
fetch('https://api2.example.com/data').then(r => r.json()),
fetch('https://api3.example.com/data').then(r => r.json())
]);
console.log('Fastest server response:', firstResponse);
Практический пример: timeout
const fetchWithTimeout = (url, timeout = 5000) => {
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeout)
);
return Promise.race([
fetch(url).then(r => r.json()),
timeoutPromise
]);
};
try {
const data = await fetchWithTimeout('/api/data', 3000);
console.log('Data:', data);
} catch (error) {
console.error(error.message); // "Request timeout" если не ответил
}
Преимущества:
- Хорош для случаев, когда нужен первый результат
- Может использоваться для timeout'ов
Недостатки:
- Другие запросы все равно продолжают выполняться
- Результаты других запросов игнорируются
Способ 4: Async/Await с независимыми запросами
Если запросы независимы, но нужно их запустить параллельно:
async function loadMultipleResources() {
// Запускаем все три запроса одновременно
const usersPromise = fetch('/api/users').then(r => r.json());
const postsPromise = fetch('/api/posts').then(r => r.json());
const commentsPromise = fetch('/api/comments').then(r => r.json());
// Ждем все результаты
const users = await usersPromise;
const posts = await postsPromise;
const comments = await commentsPromise;
return { users, posts, comments };
}
Важно: нужно запустить все промисы ДО await!
// Плохо: запросы выполняются последовательно (медленно)
async function badExample() {
const users = await fetch('/api/users').then(r => r.json());
const posts = await fetch('/api/posts').then(r => r.json());
const comments = await fetch('/api/comments').then(r => r.json());
// Время: 3 * delay (последовательно)
}
// Хорошо: запросы выполняются параллельно (быстро)
async function goodExample() {
const p1 = fetch('/api/users').then(r => r.json());
const p2 = fetch('/api/posts').then(r => r.json());
const p3 = fetch('/api/comments').then(r => r.json());
const [users, posts, comments] = await Promise.all([p1, p2, p3]);
// Время: 1 * delay (параллельно)
}
Способ 5: Комбинированный подход — часть критична, часть нет
Критичные запросы в Promise.all(), второстепенные отдельно:
async function loadPageWithPriority() {
// Критичные данные
const [user, posts] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]);
// Второстепенные данные загружаются в фоне
const recommendationsPromise = fetch('/api/recommendations')
.then(r => r.json())
.catch(() => []); // Если упадет, возвращаем пустой массив
return {
user,
posts,
recommendations: await recommendationsPromise
};
}
Способ 6: Обработка ошибок в Promise.all()
async function loadDataWithErrorHandling() {
try {
const [data1, data2, data3] = await Promise.all([
fetch('/api/1').then(r => r.json()),
fetch('/api/2').then(r => r.json()),
fetch('/api/3').then(r => r.json())
]);
return { data1, data2, data3 };
} catch (error) {
console.error('One of the requests failed:', error);
// Можно переспросить или вернуть дефолтные значения
return {
data1: null,
data2: null,
data3: null
};
}
}
Способ 7: Обработка с fallback
const fetchWithFallback = async (url, fallback) => {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
} catch (error) {
console.warn(`Failed to fetch ${url}:`, error);
return fallback;
}
};
async function loadDashboard() {
const [users, posts, comments] = await Promise.all([
fetchWithFallback('/api/users', []),
fetchWithFallback('/api/posts', []),
fetchWithFallback('/api/comments', [])
]);
return { users, posts, comments };
}
Практический пример: React компонент
function Dashboard() {
const [data, setData] = useState({ users: [], posts: [], comments: [] });
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const loadData = async () => {
try {
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json())
]);
setData({ users, posts, comments });
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
loadData();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h1>Users ({data.users.length})</h1>
<h1>Posts ({data.posts.length})</h1>
<h1>Comments ({data.comments.length})</h1>
</div>
);
}
Сравнение способов
// 1. Promise.all() — все или ничего
await Promise.all([p1, p2, p3]);
// Быстрый отказ, если хоть один упадет
// 2. Promise.allSettled() — все результаты
await Promise.allSettled([p1, p2, p3]);
// Ждет все, неважно упали или нет
// 3. Promise.race() — первый результат
await Promise.race([p1, p2, p3]);
// Возвращает результат первого завершившегося
// 4. Частичный Promise.all()
await Promise.all([p1, p2]);
const p3Result = await p3; // Отдельно
// Разные стратегии для разных запросов
Заключение
Выбери метод:
- Все запросы критичны → Promise.all()
- Часть некритична → Promise.allSettled() или смешанный подход
- Нужен первый результат → Promise.race()
- Нужен контроль над порядком → запусти промисы, потом await
Лучшая практика:
- Запусти все асинхронные операции ОДНОВРЕМЕННО
- Используй Promise.all() для критичных данных
- Обработай ошибки корректно
- Дай feedback пользователю (loading/error states)