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

Базовое понимание отмены запросов

В TanStack Query отмена запроса рассматривается как часть жизненного цикла асинхронной операции, тесно связанная с управлением состоянием запроса, кэшем и конкурентным выполнением. Отмена позволяет прервать уже запущенный запрос до его завершения, освобождая сетевые ресурсы и предотвращая обновление состояния устаревшими данными.

Ключевая особенность архитектуры заключается в том, что библиотека не «прерывает» запрос напрямую, а передаёт механизмы отмены в функцию queryFn, предоставляя разработчику контроль над тем, как именно реализуется прерывание.


AbortController как основа отмены

Основной механизм отмены в современных версиях TanStack Query строится на стандарте AbortController.

Запрос получает объект signal, который позволяет:

  • отслеживать факт отмены
  • прерывать fetch-операции
  • выбрасывать исключение при отмене
const fetchUser = async ({ signal }) => {
  const res = await fetch('/api/user', { signal })
  return res.json()
}

В данном случае signal передаётся автоматически, если queryFn написан в соответствии с контрактом TanStack Query.


Передача signal в queryFn

TanStack Query передаёт signal в объекте контекста, который приходит в queryFn.

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

const fetchUser = async ({ signal }) => {
  const res = await fetch('/api/user', { signal })
  return res.json()
}

function User() {
  const query = useQuery({
    queryKey: ['user'],
    queryFn: fetchUser
  })

  return query.data?.name
}

Поведение при отмене

Если запрос был отменён:

  • fetch автоматически прерывается
  • промис отклоняется с AbortError
  • TanStack Query помечает запрос как cancelled
  • результат не попадает в кэш

Сценарии автоматической отмены

Отмена запросов в TanStack Query происходит не только вручную, но и автоматически в ряде случаев.

1. Переключение queryKey

При изменении ключа запроса предыдущий запрос может быть отменён, если он ещё выполняется.

useQuery({
  queryKey: ['user', userId],
  queryFn: fetchUser
})

При изменении userId:

  • предыдущий запрос может быть отменён
  • запускается новый запрос с актуальным ключом

2. Дедупликация запросов

Если несколько компонентов одновременно запрашивают одни и те же данные, TanStack Query использует один запрос. При исчезновении всех подписчиков возможна отмена активного запроса.


3. Unmount компонента

При размонтировании компонента:

  • подписка удаляется
  • если нет других подписчиков, запрос может быть отменён
  • результат не обновляет состояние несуществующего компонента

Ручная отмена через QueryClient

TanStack Query предоставляет возможность вручную отменить запрос через QueryClient.

import { QueryClient } from '@tanstack/react-query'

const queryClient = new QueryClient()

queryClient.cancelQueries({ queryKey: ['user'] })

Поведение cancelQueries

При вызове:

  • активные запросы получают сигнал отмены
  • pending queryFn прерывается (если поддерживает signal)
  • состояние запроса обновляется как cancelled

Отличие отмены от игнорирования результата

Важный архитектурный момент: отмена запроса не всегда означает физическое прекращение выполнения.

Если queryFn не использует signal, возможны два сценария:

1. Логическая отмена

TanStack Query помечает запрос как отменённый, но:

  • сетевой запрос может продолжить выполняться
  • результат будет проигнорирован

2. Физическая отмена

Если используется AbortController:

  • запрос прерывается на уровне сети
  • ресурс освобождается немедленно

Работа с Axios и отменой

При использовании axios требуется явная интеграция AbortSignal.

import axios from 'axios'

const fetchUser = async ({ signal }) => {
  const res = await axios.get('/api/user', { signal })
  return res.data
}

В современных версиях axios поддержка AbortSignal встроена, что делает интеграцию с TanStack Query прямолинейной.


Отмена мутаций

Отмена применяется не только к query, но и к mutation.

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

const mutation = useMutation({
  mutationFn: async ({ signal, data }) => {
    const res = await fetch('/api/update', {
      method: 'POST',
      body: JSON.stringify(data),
      signal
    })
    return res.json()
  }
})

Особенности отмены мутаций

  • отмена не откатывает серверное состояние
  • отменённая мутация считается завершённой с ошибкой
  • кэш не обновляется
  • optimistic updates могут потребовать ручного отката

Конкурентные запросы и race conditions

Отмена тесно связана с предотвращением гонок запросов.

При частом изменении параметров (например, ввод в поиске):

useQuery({
  queryKey: ['search', query],
  queryFn: fetchSearch
})

поведение следующее:

  • старые запросы отменяются
  • новые запускаются мгновенно
  • только последний результат считается валидным

Это предотвращает ситуацию, когда медленный ответ перезаписывает новый.


Поведение при refetch

При refetch текущий активный запрос может быть отменён перед запуском нового.

const { refetch } = useQuery({
  queryKey: ['user'],
  queryFn: fetchUser
})

При вызове:

refetch()

происходит:

  • отмена текущего запроса (если он в процессе)
  • запуск нового запроса
  • обновление кэша после успешного ответа

Влияние staleTime и cacheTime на отмену

Параметры кэша влияют на частоту отмены и перезапуска запросов.

staleTime

Если данные не устарели:

  • повторный запрос не запускается
  • отмена не происходит

cacheTime

При удалении данных из кэша:

  • активные подписки прекращаются
  • возможна отмена фоновых запросов

AbortError и обработка ошибок

При отмене запросов возникает AbortError, который TanStack Query трактует особым образом.

if (error.name === 'AbortError') {
  // запрос отменён
}

Особенности:

  • не считается критической ошибкой
  • не триггерит retry
  • не попадает в error boundary (в зависимости от конфигурации)

retry и отменённые запросы

Механизм повторных попыток игнорирует отменённые запросы.

При retry:

  • отменённый запрос не повторяется автоматически
  • retry применяется только к реальным ошибкам сети

Практика оптимизации отмены

Эффективное использование отмены позволяет снизить нагрузку на сеть и сервер:

  • поисковые запросы с debounce + cancel
  • динамические фильтры таблиц
  • автокомплит
  • переключение вкладок с данными
  • real-time интерфейсы с частыми обновлениями

Типичные ошибки при работе с отменой

1. Игнорирование signal

Запрос продолжает выполняться даже после отмены, если signal не используется.

2. Неправильная работа с axios

Использование устаревших cancel token вместо AbortSignal.

3. Побочные эффекты после отмены

Вызовы setState или обработка данных после отмены запроса.

4. Смешивание отмены и бизнес-ошибок

AbortError не должен обрабатываться как ошибка логики приложения.


Внутренний жизненный цикл отмены

Процесс отмены можно разложить на этапы:

  1. Инициирование отмены (смена key, unmount, cancelQueries)
  2. Отправка AbortSignal в queryFn
  3. Генерация AbortError (если поддерживается)
  4. Остановка выполнения запроса
  5. Очистка подписок
  6. Обновление состояния запроса в кэше

Итоговая модель поведения

Отмена в TanStack Query представляет собой координированный механизм:

  • библиотека управляет жизненным циклом запроса
  • AbortController обеспечивает низкоуровневое прерывание
  • разработчик контролирует только реализацию queryFn
  • результатом является консистентное состояние без гонок данных