|$ curl https://forge-ai.dev/api/markdown?path=docs/js/abort-controller
$cat docs/abortcontroller-&-abortsignal.md
updated Recently·16 min read·published
AbortController & AbortSignal
◆JavaScript◆Intermediate
Introduction
AbortController and AbortSignal provide a standardized way to abort asynchronous operations — fetch requests, streams, event listeners, and custom async tasks. The pattern is built into the Fetch API, ReadableStream, and increasingly adopted by third-party libraries.
Basic Usage
Create an AbortController, pass its signal to fetch, then call abort() to cancel.
abort-fetch.js
JavaScript
| 1 | const controller = new AbortController(); |
| 2 | const { signal } = controller; |
| 3 | |
| 4 | // Start a fetch that can be aborted |
| 5 | fetch("/api/data", { signal }) |
| 6 | .then(res => res.json()) |
| 7 | .then(data => console.log(data)) |
| 8 | .catch(err => { |
| 9 | if (err.name === "AbortError") { |
| 10 | console.log("Request was cancelled"); |
| 11 | } else { |
| 12 | console.error("Other error:", err); |
| 13 | } |
| 14 | }); |
| 15 | |
| 16 | // Cancel the request |
| 17 | controller.abort(); |
| 18 | // Throws DOMException: AbortError |
Timeout Pattern
Combine AbortSignal with timeout for race-against-time scenarios.
abort-timeout.js
JavaScript
| 1 | function fetchWithTimeout(url, ms = 5000) { |
| 2 | const controller = new AbortController(); |
| 3 | const timeoutId = setTimeout(() => controller.abort(), ms); |
| 4 | |
| 5 | return fetch(url, { signal: controller.signal }) |
| 6 | .finally(() => clearTimeout(timeoutId)); |
| 7 | } |
| 8 | |
| 9 | // Usage |
| 10 | try { |
| 11 | const res = await fetchWithTimeout("/api/data", 3000); |
| 12 | const data = await res.json(); |
| 13 | } catch (err) { |
| 14 | if (err.name === "AbortError") { |
| 15 | console.error("Request timed out"); |
| 16 | } |
| 17 | } |
| 18 | |
| 19 | // Static timeout helper (Chrome 103+, Node 18+) |
| 20 | const res = await fetch("/api/data", { |
| 21 | signal: AbortSignal.timeout(5000), |
| 22 | }); |
Custom Abortable Tasks
Integrate AbortSignal into your own async patterns.
abort-custom.js
JavaScript
| 1 | function wait(ms, { signal } = {}) { |
| 2 | return new Promise((resolve, reject) => { |
| 3 | if (signal?.aborted) { |
| 4 | return reject(new DOMException("Aborted", "AbortError")); |
| 5 | } |
| 6 | |
| 7 | const timer = setTimeout(resolve, ms); |
| 8 | |
| 9 | signal?.addEventListener("abort", () => { |
| 10 | clearTimeout(timer); |
| 11 | reject(new DOMException("Aborted", "AbortError")); |
| 12 | }, { once: true }); |
| 13 | }); |
| 14 | } |
| 15 | |
| 16 | const controller = new AbortController(); |
| 17 | setTimeout(() => controller.abort(), 500); |
| 18 | |
| 19 | try { |
| 20 | await wait(2000, { signal: controller.signal }); |
| 21 | } catch { |
| 22 | console.log("Wait was aborted"); |
| 23 | } |
ℹ
info
Always check signal.aborted synchronously before starting async work. Listen to the abort event to clean up resources (timers, streams, connections) immediately.