Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як скасувати виконання проміса?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Проміс** у JavaScript не можна скасувати напряму: він починає виконуватися одразу після створення, і стандартного механізму зупинити таймер чи `fetch` немає, адже проміс - це не "потік", а обгортка над результатом, який колись прийде. **Ключове:** щоб "скасувати" проміс, потрібно скасовувати саму дію, яку він представляє - за допомогою `AbortController`, таймаутів або прапорців відмови.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Чому проміс не можна скасувати напряму Коли ви створюєте проміс, наприклад: ```javascript const promise = new Promise((resolve) => { setTimeout(() => resolve('done'), 5000); }); ``` Він **починає виконуватися одразу** (з моменту виклику конструктора), і жодного стандартного механізму, щоб "зупинити" таймер чи `fetch`, немає. Тобто проміс - це не "потік", а **обгортка над результатом**, який колись прийде. --- ## Способи реалізації скасування ### 1. Використати `AbortController` (вбудований стандарт) Це найсучасніший і "правильний" спосіб скасування асинхронних операцій, особливо мережевих (`fetch`). ```javascript const controller = new AbortController(); const { signal } = controller; fetch('/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); ``` Що відбувається: - `AbortController` створює об'єкт керування; - `signal` передається у `fetch`; - при `controller.abort()` запит переривається, і проміс завершується з `AbortError`. Працює з: - `fetch()` - `ReadableStream`, `Response`, `Request` - багатьма бібліотеками, що підтримують сигнали (Axios, Deno, Node 18+ тощо) --- ### 2. Створити "скасовуваний" проміс вручну Можна обгорнути звичайний проміс у структуру з можливістю скасування. ```javascript function makeCancellable(promise) { let isCancelled = false; const wrapped = new Promise((resolve, reject) => { promise .then((val) => (isCancelled ? reject({ cancelled: true }) : resolve(val))) .catch((error) => (isCancelled ? reject({ cancelled: true }) : reject(error))); }); return { promise: wrapped, cancel() { isCancelled = true; }, }; } ``` Приклад використання: ```javascript const { promise, cancel } = makeCancellable( new Promise((resolve) => setTimeout(() => resolve('Завершено'), 5000)) ); promise .then(console.log) .catch((e) => { if (e.cancelled) console.log('Скасовано користувачем'); }); setTimeout(cancel, 2000); ``` Через 2 секунди проміс буде "скасований". Важливо: це **логічне скасування**, сама операція (наприклад `setTimeout` чи `fetch`) все одно триває. --- ### 3. Використати прапорець всередині async-функції Якщо у вас є `async`-функція, можна перевіряти прапорець "скасування": ```javascript let cancelled = false; async function runTask() { for (let i = 0; i < 5; i++) { if (cancelled) throw new Error('Скасовано'); await new Promise(r => setTimeout(r, 1000)); console.log('Крок', i); } console.log('Готово'); } const task = runTask(); setTimeout(() => (cancelled = true), 2500); ``` Після 2.5 секунди виконання припиниться. --- ### 4. Комбінувати з `Promise.race()` (скасування через "гонку") Можна влаштувати "гонку" між операцією і таймером скасування: ```javascript function cancelable(promise, ms) { const cancel = new Promise((_, reject) => setTimeout(() => reject(new Error('Таймаут')), ms) ); return Promise.race([promise, cancel]); } cancelable(fetch('/api/data'), 2000) .then(res => res.json()) .then(console.log) .catch(console.error); ``` Якщо `fetch` не встигне за 2 секунди, проміс завершиться з помилкою "Таймаут". --- ## Підсумок | Метод | Що робить | Справжнє скасування | Підходить для | |---|---|---|---| | **AbortController** | Перериває операції, що підтримують сигнали | Так | `fetch`, API з `signal` | | **makeCancellable()** | Створює обгортку з прапорцем | Ні (логічне скасування) | Користувацькі проміси | | **Прапорець скасування** | Перевіряє "cancelled" вручну | Ні | Цикли, async-функції | | **Promise.race()** | Завершується першим спрацьованим промісом | Ні, але зручно | Таймаути, конкурентні завдання | --- **Підсумкове правило:** > "Скасувати" проміс у JS напряму не можна. > Але можна **скасувати дію**, яку він представляє, > за допомогою `AbortController`, таймаутів або прапорців скасування.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.