← Назад к вопросам

Делал ли последовательные запросы

2.2 Middle🔥 111 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Последовательные запросы в фронтенде

Это очень популярный вопрос на собеседованиях. Работодатель проверяет, понимаете ли вы асинхронность, обработку ошибок и оптимизацию. Рассмотрим все варианты реализации.

Что такое последовательные запросы?

Последовательные запросы — это когда второй запрос зависит от результата первого. Например:

  1. Получить данные пользователя
  2. Получить заказы этого пользователя
  3. Получить статусы доставки для каждого заказа

Вариант 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 };
}

На собеседовании

Ответьте: "Да, я делал последовательные запросы. Например, при загрузке профиля пользователя:

  1. Сначала получаю данные пользователя
  2. Потом загружаю его заказы (зависит от user.id)
  3. Потом получаю адреса доставки для каждого заказа

Использую async/await для читаемости. Если запросы независимы — выполняю их параллельно с Promise.all(). Обязательно обрабатываю ошибки и показываю loading состояние пользователю."

Делал ли последовательные запросы | PrepBro