Skip to main content

AbortController у JavaScript

AbortController це спеціальний об'єкт, який дозволяє створити «сигнал скасування» (AbortSignal) і надіслати команду «скасувати» асинхронним операціям, що цей сигнал слухають. Простіше кажучи: AbortController керує станом «скасовано чи ні», а AbortSignal це прапорець, який асинхронний код може відстежувати.

Теорія

TL;DR

  • new AbortController() створює контролер із двома ключовими членами: signal і abort().
  • controller.signal це об'єкт AbortSignal, який передають в асинхронну операцію.
  • controller.abort() переводить сигнал у стан «скасовано» і викликає всіх його слухачів.
  • У fetch скасований запит відхиляється помилкою з err.name === 'AbortError'.
  • Сигнал можна підтримати і у власних асинхронних функціях: читати signal.aborted та слухати подію abort.
  • Контролер одноразовий: для наступної операції потрібен новий екземпляр.

Швидкий приклад

javascript
const controller = new AbortController(); // створюємо контролер const signal = controller.signal; // дістаємо з нього сигнал fetch('https://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); } }); // Скасуємо запит через 1 секунду: setTimeout(() => { controller.abort(); // скасування }, 1000);

Що відбувається:

  1. Створюємо контролер, далі controller.signal передаємо у fetch.
  2. Коли викликаємо controller.abort(), fetch миттєво переривається і викидає помилку AbortError.

Як це працює під капотом

AbortController створює об'єкт із двома ключовими властивостями:

  • signal це об'єкт AbortSignal, який можна передати в асинхронну операцію;
  • abort() це метод, який активує цей сигнал: змінює його стан і викликає всі зареєстровані обробники.

Сам контролер нічого не вміє скасовувати. Він лише перемикає прапорець, а реальну роботу з переривання виконує той API, якому ви віддали сигнал.

Що вміє AbortSignal

AbortSignal:

  • має властивість .aborted (булеве значення: true, якщо операцію скасовано);
  • може слухати подію "abort".
javascript
const controller = new AbortController(); const signal = controller.signal; signal.addEventListener('abort', () => { console.log('Операцію скасовано!'); }); controller.abort(); // => "Операцію скасовано!"

Де можна використовувати

AbortController підтримується багатьма API:

APIПідтримує скасування?
fetch()Так
ReadableStream, WritableStreamТак
WebSocket (через власну обгортку)Частково
setTimeout / setInterval (через AbortSignal API у Node 20+ або браузерний polyfill)Так
navigator.sendBeacon() (у деяких браузерах)Частково
Власні async функціїТак, якщо реалізувати підтримку вручну

Приклад з Promise і власним скасуванням

Сигнал можна використовувати у своїх асинхронних функціях:

javascript
function wait(ms, signal) { return new Promise((resolve, reject) => { const timeout = setTimeout(resolve, ms); signal.addEventListener('abort', () => { clearTimeout(timeout); reject(new Error('Скасовано користувачем')); }); }); } const controller = new AbortController(); wait(5000, controller.signal) .then(() => console.log('Минуло 5 секунд')) .catch(err => console.log(err.message)); setTimeout(() => controller.abort(), 2000); // скасовуємо через 2 секунди

Результат:

text
Скасовано користувачем

Повторне використання і підсумок API

Контролер одноразовий:

  • після controller.abort() його signal назавжди отримує .aborted = true;
  • щоб скасувати ще раз, потрібно створити новий AbortController.
Властивість або методЩо робить
new AbortController()створює контролер
.signalсигнал для передавання в async API
.abort()надсилає команду «скасувати»
signal.abortedtrue, якщо скасовано
signal.addEventListener('abort', fn)слухає подію скасування

Типові помилки

  • Перевикористання одного контролера для кількох запитів. Після першого abort() сигнал уже «скасований», тож наступний fetch із ним впаде одразу. Створюйте новий контролер на кожен запит.
  • Ковтання AbortError як звичайної помилки. У catch треба окремо перевіряти err.name === 'AbortError', інакше користувач побачить повідомлення про збій там, де запит просто скасували свідомо.
  • Очікування, що abort() скасує роботу на сервері. Скасовується лише клієнтська частина: браузер обриває з'єднання, але сервер міг уже виконати операцію.
  • Забутий слухач abort. У власних функціях слухача варто прибирати після завершення роботи (наприклад, через { once: true }), інакше сигнал з довгим життям утримуватиме посилання і спричинить витік пам'яті.
  • Ігнорування вже скасованого сигналу. Якщо ви пишете власну async функцію, на початку перевіряйте signal.aborted, бо сигнал міг бути скасований ще до її виклику.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.