Stop unnecessary network calls, prevent race conditions, and make your app snappier with these practical examples.

Introduction
Here’s a dirty secret:
Even experienced developers let old Fetch requests keep running in the background, wasting time, bandwidth, and sometimes even breaking the UI.
That’s why the AbortController API exists to give you full control over in-flight requests.
It’s simple:
- Create an
AbortController
- Pass its
signal
tofetch()
- Call
abort()
When you want to cancel the request
Let’s see 5 real-world examples where using it makes your app faster, cleaner, and smarter.
Example 1: Canceling a Search While Typing
Every keystroke shouldn’t trigger a new API call that runs to completion.
You only care about the latest query.
let controller;
async function handleSearch(query) {
// Cancel previous request if still active
if (controller) controller.abort();
controller = new AbortController();
try {
const res = await fetch(`/api/search?q=${query}`, {
signal: controller.signal,
});
const data = await res.json();
console.log("Search results:", data);
} catch (err) {
if (err.name === "AbortError") {
console.log("Old request canceled");
} else {
console.error(err);
}
}
}
// Called on every input change
handleSearch("rea");
handleSearch("react");
✅ Cancels old requests instantly
✅ Prevents race conditions in search suggestions
Example 2: Cleaning Up in React Components
When a component unmounts (like navigating to another page), any ongoing Fetch should be aborted, or you’ll get “state update on unmounted component” warnings.
import { useEffect, useState } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${userId}`, { signal: controller.signal })
.then((res) => res.json())
.then(setUser)
.catch((err) => {
if (err.name !== "AbortError") console.error(err);
});
// Cancel request when unmounting or userId changes
return () => controller.abort();
}, [userId]);
return user ? <p>{user.name}</p> : <p>Loading...</p>;
}
✅ Cancels requests automatically when navigating away
✅ Prevents memory leaks
✅ Keeps your React app clean and stable
Example 3: Timeout for Slow APIs
Some APIs hang forever.
You can combine AbortController
with setTimeout()
to cancel after a limit.
async function fetchWithTimeout(url, ms = 5000) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), ms);
try {
const res = await fetch(url, { signal: controller.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
if (err.name === "AbortError") {
console.error("Request timed out!");
} else {
console.error("Fetch failed:", err);
}
} finally {
clearTimeout(timeout);
}
}
fetchWithTimeout("/api/data", 3000);
✅ Avoids “infinite loading”
✅ Gives users faster feedback
✅ Useful for flaky or slow backends
Example 4: Canceling Multiple Requests Together
Sometimes you want to cancel a group of requests, for example, when a dashboard page changes or a tab closes.
const controller = new AbortController();
async function loadDashboard() {
try {
const [posts, comments, users] = await Promise.all([
fetch("/api/posts", { signal: controller.signal }),
fetch("/api/comments", { signal: controller.signal }),
fetch("/api/users", { signal: controller.signal }),
]);
console.log("All loaded!");
} catch (err) {
if (err.name === "AbortError") console.log("All requests canceled");
else console.error(err);
}
}
// Cancel them all at once
controller.abort();
✅ Cancels all pending fetches instantly
✅ Saves bandwidth and prevents partial data states
Example 5: Canceling Background Sync or Polling
If your app polls for updates (e.g., notifications), you should stop polling when the user leaves or the tab loses focus.
let controller;
function startPolling() {
controller = new AbortController();
const poll = async () => {
try {
const res = await fetch("/api/notifications", {
signal: controller.signal,
});
const data = await res.json();
console.log("Notifications:", data);
// Repeat every 5s
setTimeout(poll, 5000);
} catch (err) {
if (err.name === "AbortError") {
console.log("Polling stopped");
} else {
console.error(err);
}
}
};
poll();
}
function stopPolling() {
if (controller) controller.abort();
}
startPolling();
// Later...
stopPolling();
✅ Stops background polling immediately
✅ Prevents wasteful network requests when not needed
Common Gotchas
⚠️ Don’t reuse a controller once aborted; it’s permanently “done.”
⚠️ Always check err.name === "AbortError"
to handle cancellations cleanly.
⚠️ Pass signal
to fetch()
abort won’t work otherwise.
Why It Matters
Modern apps are dynamic; users navigate fast, type often, and expect instant updates.
If you’re not canceling redundant requests, your app is:
- Slower
- More resource-hungry
- Prone to data flickering
AbortController
is the difference between a reactive UI and a responsible one.
✅ Saves bandwidth
✅ Prevents race conditions
✅ Cleans up async tasks
✅ Improves overall UX
Conclusion
The AbortController
API is small but mighty.
It’s the easiest way to control async operations like a pro.
✅ Quick recap:
- Cancel old search requests instantly
- Clean up Fetch in React
- Add custom timeouts
- Cancel multiple requests at once
- Stop background polling gracefully
Don’t just send requests manage them intelligently.
Once you start canceling unnecessary Fetch calls, you’ll never go back.
Call to Action
Have you ever used AbortController
In your apps?
Share your favorite use case (or a bug it helped you fix) in the comments 👇
And if your teammate’s app still makes 10 requests per second for no reason…
Send them this post before their browser melts.
Leave a Reply