← Назад к вопросам
Делал ли последовательные запросы
2.2 Middle🔥 111 комментариев
#Браузер и сетевые технологии
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Последовательные запросы в фронтенде
Это очень популярный вопрос на собеседованиях. Работодатель проверяет, понимаете ли вы асинхронность, обработку ошибок и оптимизацию. Рассмотрим все варианты реализации.
Что такое последовательные запросы?
Последовательные запросы — это когда второй запрос зависит от результата первого. Например:
- Получить данные пользователя
- Получить заказы этого пользователя
- Получить статусы доставки для каждого заказа
Вариант 1: Callbacks (устарело, но нужно знать)
function fetchUserData() {
fetch('/api/users/123')
.then(response => response.json())
.then(user => {
// Второй запрос зависит от результата первого
fetch(`/api/users/${user.id}/orders`)
.then(response => response.json())
.then(orders => {
// Третий запрос
fetch(`/api/orders/${orders[0].id}/status`)
.then(response => response.json())
.then(status => {
console.log(status);
});
});
});
}
Проблемы:
- Callback hell (адская пирамида)
- Сложно обрабатывать ошибки
- Трудно читать и поддерживать
Вариант 2: Promise chains (лучше)
function fetchUserData() {
return fetch('/api/users/123')
.then(response => response.json())
.then(user => {
return fetch(`/api/users/${user.id}/orders`);
})
.then(response => response.json())
.then(orders => {
return fetch(`/api/orders/${orders[0].id}/status`);
})
.then(response => response.json())
.then(status => {
console.log('Final status:', status);
return status;
})
.catch(error => {
console.error('Error in chain:', error);
});
}
Плюсы:
- Более читаемо
- Одна точка обработки ошибок
Минусы:
- Все ещё не очень удобно
Вариант 3: Async/await (современный подход)
async function fetchUserData() {
try {
// Запрос 1
const userResponse = await fetch('/api/users/123');
const user = await userResponse.json();
// Запрос 2
const ordersResponse = await fetch(`/api/users/${user.id}/orders`);
const orders = await ordersResponse.json();
// Запрос 3
const statusResponse = await fetch(`/api/orders/${orders[0].id}/status`);
const status = await statusResponse.json();
return status;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
// Использование
const result = await fetchUserData();
Плюсы:
- Синхронный стиль, легче читать
- Простая обработка ошибок
- Легко добавить логику
Минусы:
- Выполняется последовательно, медленнее
Вариант 4: Параллельные запросы (когда это возможно)
Если запросы независимы, выполняй их параллельно:
async function fetchMultipleData() {
try {
// Запросы параллельно
const [userResponse, settingsResponse, notificationsResponse] = await Promise.all([
fetch('/api/users/123'),
fetch('/api/settings'),
fetch('/api/notifications')
]);
const user = await userResponse.json();
const settings = await settingsResponse.json();
const notifications = await notificationsResponse.json();
return { user, settings, notifications };
} catch (error) {
console.error('Error:', error);
}
}
Когда использовать параллель:
- Запросы независимы друг от друга
- Нет необходимости ждать результат одного для другого
- Нужна максимальная производительность
Вариант 5: Смешанный подход
async function fetchUserWithOrders() {
try {
// Сначала получить пользователя (зависит от него многое)
const userResponse = await fetch('/api/users/123');
const user = await userResponse.json();
// Затем параллельно получить несвязанные данные
const [ordersResponse, profileResponse] = await Promise.all([
fetch(`/api/users/${user.id}/orders`),
fetch(`/api/users/${user.id}/profile`)
]);
const orders = await ordersResponse.json();
const profile = await profileResponse.json();
return { user, orders, profile };
} catch (error) {
console.error('Error:', error);
}
}
Практический пример: форма с динамическими данными
function CitySelector() {
const [countries, setCountries] = useState([]);
const [cities, setCities] = useState([]);
const [selectedCountry, setSelectedCountry] = useState('');
const [selectedCity, setSelectedCity] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// Загрузить страны при монтировании
useEffect(() => {
const fetchCountries = async () => {
try {
setLoading(true);
const response = await fetch('/api/countries');
const data = await response.json();
setCountries(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchCountries();
}, []);
// Загрузить города при изменении страны
useEffect(() => {
if (!selectedCountry) {
setCities([]);
return;
}
const fetchCities = async () => {
try {
setLoading(true);
const response = await fetch(`/api/countries/${selectedCountry}/cities`);
const data = await response.json();
setCities(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchCities();
}, [selectedCountry]);
if (error) return <p>Ошибка: {error}</p>;
return (
<div>
<select
value={selectedCountry}
onChange={(e) => setSelectedCountry(e.target.value)}
disabled={loading}
>
<option value="">Выберите страну</option>
{countries.map(country => (
<option key={country.id} value={country.id}>
{country.name}
</option>
))}
</select>
<select
value={selectedCity}
onChange={(e) => setSelectedCity(e.target.value)}
disabled={loading || !selectedCountry}
>
<option value="">Выберите город</option>
{cities.map(city => (
<option key={city.id} value={city.id}>
{city.name}
</option>
))}
</select>
</div>
);
}
Обработка ошибок при последовательных запросах
async function robustFetch() {
const maxRetries = 3;
let attempts = 0;
while (attempts < maxRetries) {
try {
attempts++;
const userResponse = await fetch('/api/users/123');
if (!userResponse.ok) {
throw new Error(`HTTP error! status: ${userResponse.status}`);
}
const user = await userResponse.json();
const ordersResponse = await fetch(`/api/users/${user.id}/orders`);
if (!ordersResponse.ok) {
throw new Error(`Failed to fetch orders`);
}
const orders = await ordersResponse.json();
return { user, orders };
} catch (error) {
console.error(`Attempt ${attempts} failed:`, error);
if (attempts >= maxRetries) {
throw new Error(`Failed after ${maxRetries} attempts: ${error.message}`);
}
// Ждём перед следующей попыткой
await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
}
}
}
Оптимизация с кешированием
const cache = new Map();
async function fetchWithCache(url) {
if (cache.has(url)) {
console.log('From cache:', url);
return cache.get(url);
}
const response = await fetch(url);
const data = await response.json();
cache.set(url, data);
return data;
}
async function getUserWithOrders(userId) {
const user = await fetchWithCache(`/api/users/${userId}`);
const orders = await fetchWithCache(`/api/users/${userId}/orders`);
return { user, orders };
}
На собеседовании
Ответьте: "Да, я делал последовательные запросы. Например, при загрузке профиля пользователя:
- Сначала получаю данные пользователя
- Потом загружаю его заказы (зависит от user.id)
- Потом получаю адреса доставки для каждого заказа
Использую async/await для читаемости. Если запросы независимы — выполняю их параллельно с Promise.all(). Обязательно обрабатываю ошибки и показываю loading состояние пользователю."