← Назад к вопросам
Как выполнить два запроса параллельно?
1.0 Junior🔥 71 комментариев
#Браузер и сетевые технологии
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как выполнить два запроса параллельно в JavaScript
Параллельное выполнение запросов — это основной паттерн в веб-разработке. Вместо того чтобы ждать первого запроса, а потом делать второй, мы запускаем оба одновременно.
Основной подход: Promise.all()
Promise.all() — это самый распространённый способ:
// Запускаем оба запроса одновременно
const [users, posts] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json())
]);
console.log(users);
console.log(posts);
// Оба запроса выполняются параллельно!
// Время выполнения = max(время1, время2), а не время1 + время2
Как это работает:
// Без параллелизма — медленно (5 сек + 3 сек = 8 сек)
async function sequential() {
const users = await fetch("/api/users").then(r => r.json()); // 5 сек
const posts = await fetch("/api/posts").then(r => r.json()); // 3 сек
return { users, posts };
}
// С параллелизмом — быстро (max(5 сек, 3 сек) = 5 сек)
async function parallel() {
const [users, posts] = await Promise.all([
fetch("/api/users").then(r => r.json()), // 5 сек
fetch("/api/posts").then(r => r.json()) // 3 сек
]);
return { users, posts };
}
Вариант 1: Promise.all() — все или ничего
Promise.all() отклоняет промис, если любой из запросов ошибается:
async function fetchData() {
try {
const [users, posts] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json())
]);
console.log("Success:", users, posts);
} catch (error) {
console.error("Error:", error);
// Если один из запросов ошибся — ловим здесь
}
}
Вариант 2: Promise.allSettled() — все запросы
Promise.allSettled() ждёт всех запросов, даже если они ошибаются:
async function fetchDataSafely() {
const results = await Promise.allSettled([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json())
]);
// results = [
// { status: "fulfilled", value: [...users] },
// { status: "rejected", reason: Error(...) }
// ]
const [usersResult, postsResult] = results;
if (usersResult.status === "fulfilled") {
console.log("Users:", usersResult.value);
} else {
console.error("Users error:", usersResult.reason);
}
if (postsResult.status === "fulfilled") {
console.log("Posts:", postsResult.value);
} else {
console.error("Posts error:", postsResult.reason);
}
}
Вариант 3: Promise.race() — первый результат
Promise.race() возвращает результат первого выполненного промиса:
// Подождать первого успешного ответа
const firstResponse = await Promise.race([
fetch("/api/fast-server-1").then(r => r.json()),
fetch("/api/fast-server-2").then(r => r.json()),
fetch("/api/fast-server-3").then(r => r.json())
]);
// Ис пользование: таймаут для запроса
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), timeout)
)
]);
}
await fetchWithTimeout("/api/slow", 3000); // Либо ответ, либо Error через 3 сек
Практический пример: загрузка данных профиля
// Профиль состоит из нескольких кусков данных
async function loadUserProfile(userId) {
try {
const [profile, settings, followers] = await Promise.all([
fetch(`/api/users/${userId}`).then(r => r.json()),
fetch(`/api/users/${userId}/settings`).then(r => r.json()),
fetch(`/api/users/${userId}/followers`).then(r => r.json())
]);
return { profile, settings, followers };
} catch (error) {
console.error("Failed to load profile", error);
throw error;
}
}
// Использование
const userProfile = await loadUserProfile(123);
Объединение параллельных запросов с серией
Иногда нужна комбинация:
async function complexDataFlow() {
// 1. Сначала загружаем пользователя
const user = await fetch("/api/me").then(r => r.json());
// 2. Затем параллельно загружаем его посты и комментарии
const [posts, comments] = await Promise.all([
fetch(`/api/users/${user.id}/posts`).then(r => r.json()),
fetch(`/api/users/${user.id}/comments`).then(r => r.json())
]);
// 3. Наконец загружаем авторов каждого комментария параллельно
const authors = await Promise.all(
comments.map(comment =>
fetch(`/api/users/${comment.authorId}`).then(r => r.json())
)
);
return { user, posts, comments, authors };
}
С использованием async/await и Promise.all()
// Чистый async/await синтаксис
async function getTwoThings() {
// Запускаем оба promise одновременно
const promise1 = fetch("/api/data1").then(r => r.json());
const promise2 = fetch("/api/data2").then(r => r.json());
// Ждём оба
const [data1, data2] = await Promise.all([promise1, promise2]);
return { data1, data2 };
}
// Или более компактно
async function getTwoThingsCompact() {
return Promise.all([
fetch("/api/data1").then(r => r.json()),
fetch("/api/data2").then(r => r.json())
]);
}
Обработка ошибок с allSettled
async function robustFetch() {
const results = await Promise.allSettled([
fetch("/api/critical").then(r => r.json()),
fetch("/api/optional1").then(r => r.json()),
fetch("/api/optional2").then(r => r.json())
]);
const [critical, optional1, optional2] = results;
// Проверяем критичный запрос
if (critical.status === "rejected") {
throw new Error("Critical data failed to load");
}
// Опциональные могут быть пропущены
const data1 = optional1.status === "fulfilled" ? optional1.value : null;
const data2 = optional2.status === "fulfilled" ? optional2.value : null;
return {
critical: critical.value,
data1,
data2
};
}
Ограничение параллелизма
Иногда нельзя запускать ВСЕ запросы сразу (ограничение API):
// Максимум 3 параллельных запроса
async function fetchWithLimit(urls, limit = 3) {
const results = [];
for (let i = 0; i < urls.length; i += limit) {
const batch = urls.slice(i, i + limit);
const batchResults = await Promise.all(
batch.map(url => fetch(url).then(r => r.json()))
);
results.push(...batchResults);
}
return results;
}
const data = await fetchWithLimit(
["/api/1", "/api/2", "/api/3", "/api/4", "/api/5"],
2 // По 2 запроса за раз
);
В React компонентах
import { useEffect, useState } from "react";
function UserProfile({ userId }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Запускаем параллельные запросы
Promise.all([
fetch(`/api/users/${userId}`).then(r => r.json()),
fetch(`/api/users/${userId}/posts`).then(r => r.json())
])
.then(([user, posts]) => {
setData({ user, posts });
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{data.user.name}</h1>
<p>Posts: {data.posts.length}</p>
</div>
);
}
Таблица сравнения методов
| Метод | Поведение при ошибке | Использование |
|---|---|---|
| Promise.all() | Отклоняет сразу | Когда все запросы критичны |
| Promise.allSettled() | Ждёт всех | Когда некоторые могут ошибаться |
| Promise.race() | Первый результат | Таймауты, балансировка |
Производительность
Параллельные запросы экономят время:
Последовательно:
fetch1 (5s) -> fetch2 (3s) = 8 секунд
Параллельно:
fetch1 (5s) |
fetch2 (3s) | = 5 секунд (max)
Экономия времени: 37.5%
Всегда используй Promise.all() для независимых запросов!