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

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

2.0 Middle🔥 151 комментариев
#JavaScript Core

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Инструменты для работы с асинхронными вызовами в JavaScript

В современной фронтенд-разработке работа с асинхронными операциями — это фундаментальная часть ежедневной практики. За свою карьеру я использовал различные инструменты, которые эволюционировали вместе с языком и экосистемой. Вот основные подходы и инструменты, которые я применяю:

1. Нативные механизмы JavaScript

Promise

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

// Пример использования Promise для цепочки асинхронных операций
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    console.log('Данные получены:', data);
    return processData(data);
  })
  .then(processed => saveToStorage(processed))
  .catch(error => console.error('Ошибка:', error))
  .finally(() => console.log('Запрос завершен'));

Async/Await

Мой основной инструмент для работы с асинхронным кодом, так как он делает код более читаемым и похожим на синхронный.

// Пример использования async/await
async function loadUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    const user = await response.json();
    
    const postsResponse = await fetch(`/api/users/${userId}/posts`);
    const posts = await postsResponse.json();
    
    return { user, posts };
  } catch (error) {
    console.error('Ошибка загрузки данных:', error);
    throw new Error('Не удалось загрузить данные пользователя');
  }
}

2. Специализированные библиотеки

Axios

Моя предпочтительная библиотека для HTTP-запросов благодаря удобному API, интерцепторам и встроенной поддержке отмены запросов.

import axios from 'axios';

// Создание экземпляра с настройками
const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: { 'Content-Type': 'application/json' }
});

// Использование с async/await
async function fetchData() {
  try {
    const [users, products] = await Promise.all([
      apiClient.get('/users'),
      apiClient.get('/products')
    ]);
    return { users: users.data, products: products.data };
  } catch (error) {
    // Обработка ошибок
    if (error.response) {
      // Сервер ответил с кодом ошибки
      console.error('Server error:', error.response.status);
    }
    throw error;
  }
}

React Query / TanStack Query

Для React-приложений я активно использую React Query (теперь TanStack Query) для управления серверным состоянием, кэширования, фонового обновления данных и обработки ошибок.

import { useQuery, useMutation } from '@tanstack/react-query';

// Использование useQuery для получения данных
function UserProfile({ userId }) {
  const { data, isLoading, error, refetch } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    staleTime: 5 * 60 * 1000, // 5 минут
    retry: 3,
  });

  // Использование useMutation для изменения данных
  const updateMutation = useMutation({
    mutationFn: (userData) => updateUser(userId, userData),
    onSuccess: () => {
      // Инвалидируем кэш после успешной мутации
      queryClient.invalidateQueries(['user', userId]);
    },
  });
}

3. Управление состоянием асинхронных операций

Redux с middleware

Для сложных приложений с глобальным состоянием я использую Redux вместе с middleware типа Redux Thunk или Redux Saga.

// Пример с Redux Toolkit и createAsyncThunk
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

export const fetchUserData = createAsyncThunk(
  'user/fetchData',
  async (userId, { rejectWithValue }) => {
    try {
      const response = await apiClient.get(`/users/${userId}`);
      return response.data;
    } catch (error) {
      return rejectWithValue(error.response.data);
    }
  }
);

const userSlice = createSlice({
  name: 'user',
  initialState: {
    data: null,
    loading: false,
    error: null,
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserData.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchUserData.fulfilled, (state, action) => {
        state.loading = false;
        state.data = action.payload;
      })
      .addCase(fetchUserData.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload;
      });
  },
});

4. Дополнительные утилиты и паттерны

Отмена запросов

Для предотвращения утечек памяти и race conditions я всегда реализую отмену запросов:

// Отмена fetch с AbortController
async function fetchWithTimeout(url, options = {}, timeout = 10000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal,
    });
    clearTimeout(timeoutId);
    return response;
  } catch (error) {
    clearTimeout(timeoutId);
    if (error.name === 'AbortError') {
      throw new Error('Запрос отменен по таймауту');
    }
    throw error;
  }
}

// Отмена axios запросов
const cancelTokenSource = axios.CancelToken.source();
apiClient.get('/data', {
  cancelToken: cancelTokenSource.token,
});
// Для отмены: cancelTokenSource.cancel('Запрос отменен пользователем');

Обработка параллельных запросов

// Параллельное выполнение
async function fetchDashboardData() {
  const [users, products, stats] = await Promise.all([
    fetchUsers(),
    fetchProducts(),
    fetchStats(),
  ]);
  return { users, products, stats };
}

// Последовательное выполнение с обработкой ошибок
async function sequentialOperations() {
  const results = [];
  const operations = [op1, op2, op3];
  
  for (const operation of operations) {
    try {
      const result = await operation();
      results.push(result);
    } catch (error) {
      console.error(`Ошибка в операции: ${operation.name}`, error);
      // Решаем, продолжать ли выполнение
      if (error.isCritical) throw error;
    }
  }
  
  return results;
}

5. Инструменты для отладки и мониторинга

  • Chrome DevTools: Анализ сетевых запросов, трассировка промисов
  • React DevTools: Отслеживание ререндеров, вызванных асинхронными операциями
  • Собственные хуки: Для логирования и мониторинга состояния асинхронных операций
// Кастомный хук для отслеживания состояния запроса
function useAsync(asyncFunction, immediate = true) {
  const [status, setStatus] = useState('idle');
  const [value, setValue] = useState(null);
  const [error, setError] = useState(null);
  
  const execute = useCallback(() => {
    setStatus('pending');
    setValue(null);
    setError(null);
    
    return asyncFunction()
      .then(response => {
        setValue(response);
        setStatus('success');
      })
      .catch(error => {
        setError(error);
        setStatus('error');
      });
  }, [asyncFunction]);
  
  useEffect(() => {
    if (immediate) {
      execute();
    }
  }, [execute, immediate]);
  
  return { execute, status, value, error };
}

Критерии выбора инструментов

При выборе инструментов для асинхронных вызовов я руководствуюсь несколькими принципами:

  1. Сложность приложения: Для простых приложений достаточно нативных возможностей, для сложных — специализированных библиотек
  2. Требования к кэшированию: React Query отлично подходит для сложного кэширования
  3. Архитектура состояния: Redux выбираю при наличии сложного глобального состояния
  4. Тип операций: Для побочных эффектов и сложных сценариев — Redux Saga
  5. Командные соглашения: Единый подход во всей команде важнее "идеального" инструмента

На практике я часто комбинирую эти инструменты, например, использую axios для HTTP-запросов, React Query для управления серверным состоянием, и нативные async/await для бизнес-логики. Главное — поддерживать код читаемым, обрабатывать все возможные ошибки и обеспечивать предсказуемое поведение приложения при любом сценарии выполнения асинхронных операций.