Skip to content

Commit 0451887

Browse files
Add Abort Controll Article
1 parent e70f295 commit 0451887

File tree

1 file changed

+202
-0
lines changed
  • src/pages/2025-06/advanced-abort-controller

1 file changed

+202
-0
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
---
2+
layout: "@layouts/BlogPost.astro"
3+
title: "These Advanced AbortController Features Are Amazing"
4+
date: "2025-06-23"
5+
description: "AbortController is a relatively simple API, but there are many advanced features that really empower AbortController to an amazing tool I use all the time."
6+
tags: ["JavaScript"]
7+
---
8+
9+
## Introduction
10+
11+
If you have made API requests then you have probably used `AbortController` to cancel `fetch` requests that are no longer needed. This is especially popular in frameworks like React.
12+
13+
What you probably didn't know, though, is that this is only a small percentage of what `AbortController` can do, and in this article I will be covering all the amazing features of `AbortController` that nearly no one knows.
14+
15+
_If you prefer to learn visually, check out the video version of this article._
16+
`youtube: BeZfiCPhZbI`
17+
18+
## The Basics of `AbortController`
19+
20+
_Before we dive into the advanced features I do need to cover the basics so feel free to skip this section if you are already familiar with `AbortController`._
21+
22+
The `AbortController` API works by creating a `signal` that you can pass to certain JavaScript functions. This `signal` tells the function if it should continue or abort. In order to abort a function you just need to call `controller.abort()`, and any function that is listening to that signal will stop what it is doing.
23+
24+
```js
25+
const controller = new AbortController()
26+
const signal = controller.signal
27+
28+
fetch("/api/data", { signal })
29+
.then(res => res.json())
30+
.then(data => console.log(data))
31+
.catch(err => {
32+
if (err.name === "AbortError") {
33+
console.log("Request was aborted")
34+
} else {
35+
throw err
36+
}
37+
})
38+
39+
// Cancel the request
40+
controller.abort()
41+
```
42+
43+
Fetch requests are the most common use case for `AbortController`, but it can be used with any API that supports the `signal` option.
44+
45+
## `AbortController` With Event Listeners
46+
47+
You can also use `AbortController` to remove event listeners.
48+
49+
```js
50+
const controller = new AbortController()
51+
const signal = controller.signal
52+
53+
window.addEventListener("resize", () => console.log("Resized"), {
54+
signal,
55+
})
56+
57+
// Later, this removes the listener
58+
controller.abort()
59+
```
60+
61+
When you create an event listener you can pass along a third parameter which is an options object. If you pass the `signal` property with the `AbortController`'s signal, then when you call `controller.abort()`, it will automatically remove the event listener as if you called `removeEventListener`.
62+
63+
This is especially useful in frameworks like React where you want to clean up event listeners from `useEffect`, or when you have many event listeners you want to remove at the same time.
64+
65+
```js
66+
useEffect(() => {
67+
const controller = new AbortController()
68+
const signal = controller.signal
69+
70+
window.addEventListener("dragstart", () => console.log("Drag started"), {
71+
signal,
72+
})
73+
window.addEventListener("dragend", () => console.log("Drag ended"), {
74+
signal,
75+
})
76+
77+
return () => {
78+
// Removes all the listeners
79+
controller.abort()
80+
}
81+
}, [])
82+
```
83+
84+
## `AbortSignal` Built In Functions
85+
86+
The `AbortController` API also has some built-in functions that can make your life easier through the `AbortSignal` interface.
87+
88+
### `AbortSignal.timeout()`
89+
90+
One of the most underused features of `AbortController` is `AbortSignal.timeout()`, which creates a signal that automatically aborts after a timeout:
91+
92+
```js
93+
const signal = AbortSignal.timeout(5000) // 5 seconds
94+
95+
fetch("/api/slow-endpoint", { signal }).catch(err => {
96+
if (err.name === "TimeoutError") {
97+
console.log("Request timed out")
98+
}
99+
})
100+
```
101+
102+
The above code creates a `fetch` request that will automatically abort after 5 seconds if it hasn't completed.
103+
104+
No longer do you need to manually handle timeouts with `setTimeout` and `clearTimeout`.
105+
106+
## `AbortSignal.any()`
107+
108+
Another powerful feature is `AbortSignal.any()`, which allows you to create a signal that aborts when any of the provided signals are aborted:
109+
110+
```js
111+
const controller = new AbortController()
112+
const signal = AbortSignal.any(
113+
controller.signal,
114+
AbortSignal.timeout(3000), // 3 seconds
115+
)
116+
```
117+
118+
This is great if you want to combine a timeout and a manual abort signal. The request will be aborted if either the manual abort is called or the timeout is reached.
119+
120+
## `AbortSignal.abort()`
121+
122+
You can also create an `AbortSignal` that is already aborted using `AbortSignal.abort()`:
123+
124+
```js
125+
const signal = AbortSignal.abort()
126+
fetch("/api/data", { signal }).catch(err => {
127+
if (err.name === "AbortError") {
128+
console.log("Request was aborted")
129+
}
130+
})
131+
```
132+
133+
This is probably the least useful of the built-in functions, but it can be handy in certain situations.
134+
135+
## Create Your Own `AbortController` Enabled Functions
136+
137+
The true power in `AbortController` comes from the ability to create your own functions that support aborting. This allows you to build APIs that are cancelable, just like `fetch`.
138+
139+
Doing this is as simple as accepting a signal parameter in your function, checking if it has been aborted, and then listening for the abort event:
140+
141+
```js {3-7,9-13}
142+
function doSomething(signal) {
143+
return new Promise((resolve, reject) => {
144+
// Is it already aborted?
145+
if (signal.aborted) {
146+
reject(signal.reason)
147+
return
148+
}
149+
150+
// Listen for abort events
151+
signal.addEventListener("abort", () => {
152+
clearTimeout(id)
153+
reject(signal.reason)
154+
})
155+
156+
// Simulate a long-running operation
157+
const id = setTimeout(() => resolve("Did Something"), 5000)
158+
})
159+
}
160+
```
161+
162+
Making your own abortable API is as simple as that. You can now use this function with an `AbortController`.
163+
164+
```js
165+
// Cancel the operation after 3 seconds
166+
const signal = AbortSignal.timeout(3000)
167+
168+
doSomething(signal)
169+
.then(result => console.log(result))
170+
.catch(err => {
171+
if (err.name === "AbortError") {
172+
console.log("Operation was aborted")
173+
} else {
174+
throw err
175+
}
176+
})
177+
```
178+
179+
Here is a simple function you can use to make abortable functions easier.
180+
181+
```js
182+
function makeAbortable(fn) {
183+
return signal => {
184+
return new Promise((resolve, reject) => {
185+
if (signal.aborted) {
186+
reject(signal.reason)
187+
return
188+
}
189+
190+
signal.addEventListener("abort", () => {
191+
reject(signal.reason)
192+
})
193+
194+
fn(resolve, reject, signal)
195+
})
196+
}
197+
}
198+
```
199+
200+
## Conclusion
201+
202+
`AbortController` is a powerful API that goes beyond just canceling `fetch` requests. It can be used to clean up event listeners, create timeout signals, and even build your own abortable functions.

0 commit comments

Comments
 (0)