What is AbortController?

JavaScript

The short answer

AbortController lets you cancel asynchronous operations like fetch requests. You create a controller, pass its signal to the operation, and call abort() when you want to cancel it. This is important for preventing memory leaks and race conditions, especially in React components that unmount while a request is still in progress.

How it works

const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => {
if (error.name === 'AbortError') {
console.log('Request was cancelled');
}
});
// Cancel the request
controller.abort();

When you call controller.abort(), the fetch request is cancelled and throws an AbortError. You catch it and handle it — usually by just ignoring it since cancellation is intentional.

In React — preventing state updates after unmount

The most common use in React is cleaning up fetch requests when a component unmounts:

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((data) => setUser(data))
.catch((error) => {
if (error.name !== 'AbortError') {
console.error('Fetch failed:', error);
}
});
return () => controller.abort();
}, [userId]);
if (!user) return <p>Loading...</p>;
return <h1>{user.name}</h1>;
}

The cleanup function calls controller.abort(). This handles two situations:

  1. Component unmounts — the request is cancelled, preventing a state update on an unmounted component
  2. userId changes — the previous request is cancelled before a new one starts, preventing race conditions where an old response overwrites a newer one

Preventing race conditions

Without AbortController, fast navigation can cause race conditions:

User clicks user 1 → fetch starts
User clicks user 2 → fetch starts
User 2 response arrives → displays user 2 ✓
User 1 response arrives (slower) → displays user 1 ✗ (wrong!)

With AbortController, clicking user 2 cancels the request for user 1. Only the latest request completes.

Using with async/await

async function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController();
const timeout = setTimeout(
() => controller.abort(),
timeoutMs
);
try {
const response = await fetch(url, {
signal: controller.signal,
});
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timed out');
}
throw error;
} finally {
clearTimeout(timeout);
}
}

This creates a fetch with a timeout — if the server does not respond within 5 seconds, the request is cancelled.

Interview Tip

The React useEffect cleanup example is the most important one to know. It shows you understand how to prevent memory leaks and race conditions in React. If you can explain both the unmount case and the dependency change case, that shows deep understanding of the useEffect lifecycle.

Why interviewers ask this

AbortController comes up in discussions about data fetching, memory leaks, and race conditions in React. Interviewers want to see if you know how to properly clean up async operations, especially in components that can unmount or re-render while a request is in flight. It is a practical skill that prevents real bugs in production.