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

Пользовался ли Abort Controller

2.3 Middle🔥 111 комментариев
#JavaScript Core

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

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

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

Да, я пользовался AbortController

Это один из ключевых инструментов современного фронтенда для управления асинхронными операциями. AbortController — это Web API, который позволяет прерывать веб-запросы и другие асинхронные задачи. Я активно применяю его в продакшене уже несколько лет, начиная с момента широкой поддержки браузерами.

Основные сценарии использования

1. Прерывание fetch-запросов

Самый распространённый кейс — отмена HTTP-запросов при переходе между страницами или при изменении параметров.

// Создание контроллера
const controller = new AbortController();
const { signal } = controller;

// Использование в fetch
fetch('https://api.example.com/data', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Запрос был прерван');
    } else {
      console.error('Ошибка запроса:', err);
    }
  });

// Прерывание запроса через 2 секунды
setTimeout(() => {
  controller.abort();
}, 2000);

2. Отмена нескольких запросов

Один контроллер может использоваться для нескольких операций:

const controller = new AbortController();

// Несколько параллельных запросов с одним сигналом
const requests = [
  fetch('/api/users', { signal: controller.signal }),
  fetch('/api/posts', { signal: controller.signal }),
  fetch('/api/comments', { signal: controller.signal })
];

// Отмена всех одновременно
cancelButton.addEventListener('click', () => {
  controller.abort();
});

3. Интеграция с UI

Я часто связываю AbortController с состоянием компонентов в React/Vue:

// React пример
function SearchComponent() {
  const [query, setQuery] = useState('');
  const controllerRef = useRef(null);

  useEffect(() => {
    if (query.trim() === '') return;

    // Отменяем предыдущий запрос
    if (controllerRef.current) {
      controllerRef.current.abort();
    }

    // Создаем новый контроллер
    controllerRef.current = new AbortController();

    const fetchResults = async () => {
      try {
        const response = await fetch(`/api/search?q=${query}`, {
          signal: controllerRef.current.signal
        });
        const data = await response.json();
        // Обработка результатов
      } catch (err) {
        if (err.name !== 'AbortError') {
          console.error('Search error:', err);
        }
      }
    };

    fetchResults();

    return () => {
      // Очистка при размонтировании
      if (controllerRef.current) {
        controllerRef.current.abort();
      }
    };
  }, [query]);

  return <input value={query} onChange={e => setQuery(e.target.value)} />;
}

Продвинутые техники

Кастомные асинхронные операции

AbortController можно использовать не только с fetch. Я создаю обёртки для любых асинхронных задач:

function cancellableTimeout(ms, signal) {
  return new Promise((resolve, reject) => {
    const timeoutId = setTimeout(resolve, ms);
    
    // Обработка прерывания
    if (signal.aborted) {
      clearTimeout(timeoutId);
      reject(new DOMException('Aborted', 'AbortError'));
    }
    
    signal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      reject(new DOMException('Aborted', 'AbortError'));
    });
  });
}

Композиция контроллеров

В сложных сценариях я комбинирую несколько контроллеров:

class RequestManager {
  constructor() {
    this.controllers = new Map();
  }

  startRequest(requestId, url) {
    // Отменяем существующий запрос с таким ID
    this.cancelRequest(requestId);
    
    const controller = new AbortController();
    this.controllers.set(requestId, controller);
    
    return fetch(url, { signal: controller.signal })
      .finally(() => {
        this.controllers.delete(requestId);
      });
  }

  cancelRequest(requestId) {
    const controller = this.controllers.get(requestId);
    if (controller) {
      controller.abort();
      this.controllers.delete(requestId);
    }
  }

  cancelAll() {
    for (const controller of this.controllers.values()) {
      controller.abort();
    }
    this.controllers.clear();
  }
}

Практические преимущества

  1. Улучшение производительности: Предотвращение ненужных запросов снижает нагрузку на сервер и клиент
  2. Предсказуемость состояния: Избегание race conditions при быстром изменении UI
  3. Лучший UX: Немедленная реакция на действия пользователя
  4. Снижение потребления памяти: Своевременная очистка ресурсов

Особенности реализации

Важно помнить о некоторых нюансах:

  • После вызова abort() контроллер нельзя использовать повторно
  • Сигнал остаётся в состоянии aborted навсегда
  • В React нужно быть осторожным с обновлениями состояния после размонтирования
  • Not все API поддерживают AbortSignal (например, WebSockets требуют отдельных решений)

Альтернативы и совместимость

Хотя AbortController сейчас является стандартом, в legacy-проектах я иногда сталкивался с альтернативами:

  • axios.CancelToken (устаревший подход)
  • Кастомные реализации через промисы
  • RxJS Observables с оператором takeUntil

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

Пользовался ли Abort Controller | PrepBro