Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Stacked Queries (Параллельные запросы)
Stacked queries - это техника, когда несколько запросов отправляются параллельно вместо последовательного выполнения. Это оптимизирует производительность приложения, особенно когда запросы независимы друг от друга.
1. Использование 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, posts, comments);
Преимущества:
- Все запросы выполняются одновременно
- Общее время = самый длинный запрос
- Код простой и понятный
Недостаток: Если один запрос упадёт, вся операция отклонится.
2. 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())
]);
// Обработать результаты
const users = results[0].status === 'fulfilled' ? results[0].value : null;
const posts = results[1].status === 'fulfilled' ? results[1].value : null;
const comments = results[2].status === 'fulfilled' ? results[2].value : null;
if (users) console.log('Пользователи загружены');
if (posts) console.log('Посты загружены');
if (comments) console.log('Комментарии загружены');
3. React Query для управления несколькими запросами
Solidly библиотека для кеширования и управления запросами:
import { useQuery } from '@tanstack/react-query';
function UserProfile() {
// Несколько независимых запросов
const usersQuery = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(r => r.json())
});
const postsQuery = useQuery({
queryKey: ['posts'],
queryFn: () => fetch('/api/posts').then(r => r.json())
});
const commentsQuery = useQuery({
queryKey: ['comments'],
queryFn: () => fetch('/api/comments').then(r => r.json())
});
// Проверить статусы
if (usersQuery.isLoading || postsQuery.isLoading) return <div>Загрузка...</div>;
if (usersQuery.isError || postsQuery.isError) return <div>Ошибка</div>;
return (
<div>
<h2>Пользователи: {usersQuery.data.length}</h2>
<h2>Посты: {postsQuery.data.length}</h2>
<h2>Комментарии: {commentsQuery.data.length}</h2>
</div>
);
}
4. Зависимые стекированные запросы
Когда один запрос зависит от результата другого:
// Плохо - последовательные запросы
const user = await fetch(`/api/users/${id}`).then(r => r.json());
const posts = await fetch(`/api/users/${user.id}/posts`).then(r => r.json());
// Хорошо - параллельные запросы (если известен ID)
const [user, posts] = await Promise.all([
fetch(`/api/users/${id}`).then(r => r.json()),
fetch(`/api/posts?userId=${id}`).then(r => r.json())
]);
С React Query для зависимых запросов:
function UserDetail({ userId }) {
// Первый запрос
const userQuery = useQuery({
queryKey: ['user', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json())
});
// Второй запрос зависит от первого
const postsQuery = useQuery({
queryKey: ['posts', userQuery.data?.id],
queryFn: () => fetch(`/api/posts?userId=${userQuery.data.id}`).then(r => r.json()),
enabled: !!userQuery.data // Запрос только если есть user
});
return (
<div>
<h2>{userQuery.data?.name}</h2>
<ul>
{postsQuery.data?.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
</div>
);
}
5. Практический пример: Загрузка данных профиля
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const loadData = async () => {
try {
// Отправить 4 запроса параллельно
const [user, avatar, stats, settings] = await Promise.all([
fetch(`/api/users/${userId}`).then(r => r.json()),
fetch(`/api/avatars/${userId}`).then(r => r.json()),
fetch(`/api/stats/${userId}`).then(r => r.json()),
fetch(`/api/settings/${userId}`).then(r => r.json())
]);
setData({ user, avatar, stats, settings });
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
loadData();
}, [userId]);
if (loading) return <div>Загрузка...</div>;
if (error) return <div>Ошибка: {error}</div>;
const { user, avatar, stats } = data;
return (
<div>
<h2>{user.name}</h2>
<img src={avatar.url} alt="Avatar" />
<p>Посты: {stats.postCount}</p>
</div>
);
}
6. Оптимизация с Promise.race()
Дождаться первого завершённого запроса:
// Загрузить с основного сервера или резервного
const fastestSource = await Promise.race([
fetch('https://primary-api.com/data').then(r => r.json()),
fetch('https://backup-api.com/data').then(r => r.json())
]);
console.log('Ответ с самого быстрого сервера:', fastestSource);
7. Пакетирование запросов (Request Batching)
Вместо множества отдельных запросов, отправить один пакетный запрос:
// Плохо - 10 отдельных запросов
for (let i = 0; i < 10; i++) {
await fetch(`/api/items/${i}`);
}
// Хорошо - 1 запрос с массивом
const items = await fetch('/api/items', {
method: 'POST',
body: JSON.stringify({ ids: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] })
}).then(r => r.json());
8. Дебаунсирование и кеширование запросов
class QueryCache {
constructor() {
this.cache = new Map();
}
async fetch(key, fn) {
if (this.cache.has(key)) {
return this.cache.get(key);
}
const result = await fn();
this.cache.set(key, result);
return result;
}
invalidate(key) {
this.cache.delete(key);
}
}
const cache = new QueryCache();
// Использование
const users = await cache.fetch('users', () =>
fetch('/api/users').then(r => r.json())
);
// Второй запрос вернёт кешированный результат
const users2 = await cache.fetch('users', () =>
fetch('/api/users').then(r => r.json())
);
// Инвалидировать кеш
cache.invalidate('users');
9. Отмена запросов с AbortController
const controller = new AbortController();
const promise1 = fetch('/api/users', { signal: controller.signal });
const promise2 = fetch('/api/posts', { signal: controller.signal });
const results = await Promise.allSettled([promise1, promise2]);
// Отменить все оставшиеся запросы
controller.abort();
10. Лучшие практики
Правило 1: Используй Promise.all для независимых запросов
// Независимые запросы - хорошо использовать Promise.all
await Promise.all([
fetchUsers(),
fetchSettings(),
fetchNotifications()
]);
Правило 2: Используй Promise.allSettled для устойчивости
// Когда отказ одного запроса не должен сломать приложение
const results = await Promise.allSettled([
fetchCritical(),
fetchOptional()
]);
Правило 3: Пакетируй запросы
// Вместо 50 запросов
await Promise.all(ids.map(id => fetch(`/api/item/${id}`)));
// Отправить один пакетный запрос
await fetch('/api/items/batch', {
body: JSON.stringify({ ids })
});
Правило 4: Кешируй результаты
// Не загружай одно и то же дважды
const result = cache.get(key) || await fetch(url);
Сравнение подходов
| Подход | Когда использовать | Примечание |
|---|---|---|
| Promise.all | Независимые запросы | Если один упадёт, все упадут |
| Promise.allSettled | Нужна устойчивость к ошибкам | Можно обработать частичный успех |
| React Query | Управление состоянием запросов | Встроенное кеширование |
| Promise.race | Нужна скорость | Используй редко |
| Batch API | Много мелких запросов | Эффективнее чем множество запросов |
Stacked queries - это основной способ оптимизировать загрузку данных в современных приложениях, значительно улучшая пользовательский опыт.