AbortController у JavaScript
AbortController це спеціальний об'єкт, який дозволяє створити «сигнал скасування» (AbortSignal) і надіслати команду «скасувати» асинхронним операціям, що цей сигнал слухають. Простіше кажучи: AbortController керує станом «скасовано чи ні», а AbortSignal це прапорець, який асинхронний код може відстежувати.
Теорія
TL;DR
new AbortController()створює контролер із двома ключовими членами:signalіabort().controller.signalце об'єктAbortSignal, який передають в асинхронну операцію.controller.abort()переводить сигнал у стан «скасовано» і викликає всіх його слухачів.- У
fetchскасований запит відхиляється помилкою зerr.name === 'AbortError'. - Сигнал можна підтримати і у власних асинхронних функціях: читати
signal.abortedта слухати подіюabort. - Контролер одноразовий: для наступної операції потрібен новий екземпляр.
Швидкий приклад
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);Що відбувається:
- Створюємо контролер, далі
controller.signalпередаємо уfetch. - Коли викликаємо
controller.abort(),fetchмиттєво переривається і викидає помилкуAbortError.
Як це працює під капотом
AbortController створює об'єкт із двома ключовими властивостями:
signalце об'єктAbortSignal, який можна передати в асинхронну операцію;abort()це метод, який активує цей сигнал: змінює його стан і викликає всі зареєстровані обробники.
Сам контролер нічого не вміє скасовувати. Він лише перемикає прапорець, а реальну роботу з переривання виконує той API, якому ви віддали сигнал.
Що вміє AbortSignal
AbortSignal:
- має властивість
.aborted(булеве значення:true, якщо операцію скасовано); - може слухати подію
"abort".
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 і власним скасуванням
Сигнал можна використовувати у своїх асинхронних функціях:
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 секундиРезультат:
Скасовано користувачемПовторне використання і підсумок API
Контролер одноразовий:
- після
controller.abort()йогоsignalназавжди отримує.aborted = true; - щоб скасувати ще раз, потрібно створити новий
AbortController.
| Властивість або метод | Що робить |
|---|---|
new AbortController() | створює контролер |
.signal | сигнал для передавання в async API |
.abort() | надсилає команду «скасувати» |
signal.aborted | true, якщо скасовано |
signal.addEventListener('abort', fn) | слухає подію скасування |
Типові помилки
- Перевикористання одного контролера для кількох запитів. Після першого
abort()сигнал уже «скасований», тож наступнийfetchіз ним впаде одразу. Створюйте новий контролер на кожен запит. - Ковтання
AbortErrorяк звичайної помилки. Уcatchтреба окремо перевірятиerr.name === 'AbortError', інакше користувач побачить повідомлення про збій там, де запит просто скасували свідомо. - Очікування, що
abort()скасує роботу на сервері. Скасовується лише клієнтська частина: браузер обриває з'єднання, але сервер міг уже виконати операцію. - Забутий слухач
abort. У власних функціях слухача варто прибирати після завершення роботи (наприклад, через{ once: true }), інакше сигнал з довгим життям утримуватиме посилання і спричинить витік пам'яті. - Ігнорування вже скасованого сигналу. Якщо ви пишете власну async функцію, на початку перевіряйте
signal.aborted, бо сигнал міг бути скасований ще до її виклику.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.