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

Какие знаешь методы для работы с асинхронными запросами?

2.0 Middle🔥 291 комментариев
#JavaScript Core#Браузер и сетевые технологии

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

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

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

Методы для работы с асинхронными запросами

Асинхронность - это основа современного JavaScript. Существует несколько подходов к управлению асинхронными операциями, каждый с собственными достоинствами.

1. Callback (древний способ)

// Самый первый способ работать с асинхронностью
// Функция принимает callback, который вызывается по окончании

function fetchUser(id, callback) {
  setTimeout(() => {
    const user = { id, name: 'John' };
    callback(null, user); // (error, result)
  }, 1000);
}

fetchUser(1, (error, user) => {
  if (error) {
    console.error('Ошибка:', error);
  } else {
    console.log('Пользователь:', user);
  }
});

// Проблема: Callback Hell (адская пирамида)
fetchUser(1, (err1, user) => {
  fetchPost(user.id, (err2, post) => {
    fetchComments(post.id, (err3, comments) => {
      fetchReplies(comments[0].id, (err4, replies) => {
        // Ужасный код
      });
    });
  });
});

2. Promise (стандартный способ)

// Promise - объект, который представляет результат асинхронной операции

// Создание Promise
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ id, name: 'John' }); // Успех
      } else {
        reject(new Error('Invalid id')); // Ошибка
      }
    }, 1000);
  });
}

// Использование Promise
fetchUser(1)
  .then(user => {
    console.log('Пользователь:', user);
    return fetchPost(user.id); // Цепирование
  })
  .then(post => {
    console.log('Пост:', post);
    return fetchComments(post.id);
  })
  .then(comments => {
    console.log('Комментарии:', comments);
  })
  .catch(error => {
    console.error('Произошла ошибка:', error);
  })
  .finally(() => {
    console.log('Операция завершена');
  });

// Параллельные запросы
Promise.all([
  fetchUser(1),
  fetchUser(2),
  fetchUser(3),
])
  .then(users => {
    console.log('Все пользователи:', users);
  })
  .catch(error => {
    console.error('Одна из операций упала');
  });

// Первый успешный результат
Promise.race([
  fetchUser(1),
  fetchUser(2),
])
  .then(user => {
    console.log('Первый результат:', user);
  });

3. Async/Await (современный способ)

// Это синтаксический сахар над Promise
// Кода выглядит синхронным, но работает асинхронно

async function loadUserData(id) {
  try {
    // await ждёт пока Promise resolve
    const user = await fetchUser(id);
    console.log('Пользователь:', user);
    
    // После получения user, загружаем посты
    const post = await fetchPost(user.id);
    console.log('Пост:', post);
    
    // После получения post, загружаем комментарии
    const comments = await fetchComments(post.id);
    console.log('Комментарии:', comments);
    
    // Код читается как синхронный!
    return comments;
  } catch (error) {
    console.error('Ошибка:', error);
  } finally {
    console.log('Загрузка завершена');
  }
}

// Вызов
loadUserData(1);

// Параллельные запросы с async/await
async function loadMultipleUsers() {
  try {
    // Неправильно - последовательно
    const user1 = await fetchUser(1);
    const user2 = await fetchUser(2);
    const user3 = await fetchUser(3);
    // Это займёт 3 секунды если каждый запрос 1 сек
    
    // Правильно - параллельно
    const [user1, user2, user3] = await Promise.all([
      fetchUser(1),
      fetchUser(2),
      fetchUser(3),
    ]);
    // Это займёт 1 секунду!
  } catch (error) {
    console.error('Ошибка:', error);
  }
}

4. React Hooks для асинхронности

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    // useEffect не может быть async, но может содержать async функцию
    (async () => {
      try {
        setLoading(true);
        const data = await fetchUser(userId);
        setUser(data);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    })();
    
    // Cleanup функция
    return () => {
      // Можно отменить запрос если компонент размонтировался
    };
  }, [userId]); // Зависимость от userId
  
  if (loading) return <div>Загрузка...</div>;
  if (error) return <div>Ошибка: {error.message}</div>;
  
  return <div>{user.name}</div>;
}

// Кастомный хук для загрузки данных
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    let isMounted = true;
    
    (async () => {
      try {
        const response = await fetch(url);
        const json = await response.json();
        if (isMounted) {
          setData(json);
        }
      } catch (err) {
        if (isMounted) {
          setError(err);
        }
      } finally {
        if (isMounted) {
          setLoading(false);
        }
      }
    })();
    
    return () => {
      isMounted = false; // Предотвращает setState после unmount
    };
  }, [url]);
  
  return { data, loading, error };
}

5. Fetch API и Axios

// Fetch API (встроенный в браузер)
fetch('https://api.example.com/users/1')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log('Данные:', data);
  })
  .catch(error => {
    console.error('Ошибка:', error);
  });

// Fetch с async/await
async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Ошибка:', error);
    throw error;
  }
}

// Axios (популярная библиотека)
import axios from 'axios';

const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
});

// GET запрос
api.get('/users/1')
  .then(response => {
    console.log('Данные:', response.data);
  })
  .catch(error => {
    console.error('Ошибка:', error);
  });

// POST запрос с async/await
async function createUser(name, email) {
  try {
    const response = await api.post('/users', { name, email });
    return response.data;
  } catch (error) {
    console.error('Ошибка при создании:', error.response?.data);
    throw error;
  }
}

6. RxJS и Observables (для сложных потоков)

import { from, map, switchMap } from 'rxjs';

// Observable для асинхронных потоков данных
const userSearch$ = userInput$
  .pipe(
    map(query => query.trim()),
    filter(query => query.length > 2),
    switchMap(query => fetchUsers(query)) // Отменяет предыдущий запрос
  )
  .subscribe(users => {
    console.log('Результаты поиска:', users);
  });

7. Обработка ошибок и retry

// Retry логика
async function fetchUserWithRetry(id, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fetchUser(id);
    } catch (error) {
      if (attempt === maxRetries) {
        throw error; // Последняя попытка провалилась
      }
      // Экспоненциальная задержка перед retry
      await new Promise(resolve => 
        setTimeout(resolve, 1000 * Math.pow(2, attempt - 1))
      );
    }
  }
}

// Timeout
function fetchUserWithTimeout(id, timeout = 5000) {
  return Promise.race([
    fetchUser(id),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), timeout)
    ),
  ]);
}

8. Сравнение методов

// CALLBACKS - старый способ
// + Простой для однократной операции
// - Callback hell для цепочек
// - Сложнее управлять ошибками

// PROMISES - стандартный способ
// + Можно цеплять .then()
// + Центральная обработка ошибок через .catch()
// - Иногда чит запутанным

// ASYNC/AWAIT - современный способ
// + Выглядит как синхронный код
// + Легче читать и понять
// + Простая обработка ошибок через try/catch
// - Нужно понимать что это промисы под капотом

// RxJS/OBSERVABLES - для сложных потоков
// + Мощно для управления потоками данных
// + Отмена операций, retry, debounce
// - Steep learning curve

Ответ на интервью

«Я знаю несколько методов работы с асинхронностью:

  1. Callbacks - самый старый метод, приводит к callback hell, не рекомендуется
  2. Promises - стандарт, позволяет цеплять .then(), централизованная обработка ошибок
  3. Async/await - современный сахар над Promise, код выглядит синхронным, это мой основной выбор
  4. Fetch API - встроенный в браузер инструмент для HTTP запросов
  5. Axios - популярная библиотека с автоматическим преобразованием JSON
  6. RxJS/Observables - для сложных потоков данных, отмены операций

Современный подход: async/await с Fetch API или Axios для простых запросов, RxJS если нужно управлять сложными потоками (поиск, фильтры с debounce).

Ключевые моменты:

  • Всегда обрабатывать ошибки (try/catch или .catch())
  • При параллельных запросах использовать Promise.all()
  • Быть осторожным с async operations в React useEffect (cleanup функции)
  • Использовать AbortController для отмены запросов
  • Не создавать race conditions в параллельных запросах»
Какие знаешь методы для работы с асинхронными запросами? | PrepBro