Cancel Async Requests in TypeScript
Learn to cancel in-flight async requests using AbortController with TypeScript types. Covers AbortSignal, fetch cancellation, cleanup patterns, and typed error handling.
Canceling an in-flight request means telling the browser to stop waiting for a response. In TypeScript, you do this with the AbortController API, which is fully typed in the DOM library. The pattern is straightforward: create a controller, pass its signal to fetch, and call abort when you no longer need the result.
This matters for search-as-you-type inputs, page navigations where a pending request is now stale, and component unmounts in React or similar frameworks.
The Basic AbortController Pattern
Create an AbortController instance and pass its signal property to the fetch call. The signal is typed as AbortSignal, and TypeScript knows it can be passed directly to fetch's options.
const controller = new AbortController();
fetch("/api/data", { signal: controller.signal })
.then((response) => response.json())
.catch((error) => {
if (error instanceof DOMException && error.name === "AbortError") {
console.log("Request was cancelled");
}
});
controller.abort();When abort is called, the fetch promise rejects with an AbortError. This is a DOMException, not a regular Error, so the check uses error.name instead of instanceof. The type system distinguishes AbortError from network failures, letting you handle cancellation separately.
Detecting Cancellation vs Failure
A cancelled request is not a failure. It means you deliberately stopped waiting. Separate this case from real errors in your catch block.
async function fetchWithCancel(url: string, signal: AbortSignal) {
try {
const response = await fetch(url, { signal });
return await response.json();
} catch (error) {
if (error instanceof DOMException && error.name === "AbortError") {
return null;
}
throw error;
}
}The function returns null for cancellations and re-throws actual errors. Callers can check for null to know the request was aborted. The type signature signal: AbortSignal documents that the function supports cancellation at the type level.
Cancelling on Component Unmount
In React and similar frameworks, a component might start a fetch and unmount before the response arrives. Abort the request in the cleanup function.
useEffect(() => {
const controller = new AbortController();
async function loadData() {
const data = await fetchWithCancel("/api/data", controller.signal);
if (data !== null) {
setData(data);
}
}
loadData();
return () => controller.abort();
}, []);The cleanup function runs when the component unmounts or the dependencies change. Calling abort ensures the in-flight request does not try to update state on an unmounted component. The null check after fetchWithCancel prevents the state update when the request was cancelled.
The diagram shows the two paths. Either the response arrives and state updates, or the component unmounts, the controller aborts, and the catch block safely ignores the cancelled request.
Building a Timeout Wrapper
AbortController also works for timeouts. Create a wrapper that cancels the request if it takes too long.
async function fetchWithTimeout(url: string, timeoutMs: number): Promise<Response> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
return response;
} finally {
clearTimeout(timeoutId);
}
}The timeout calls abort after the specified duration. The finally block cleans up the timeout regardless of whether the request succeeded or failed. If the request completes before the timeout, the clearTimeout prevents an unnecessary abort call.
Callers can distinguish a timeout from other failures by checking for AbortError in their catch block. The wrapper does not throw a custom error; it lets the natural AbortError propagate so callers can handle it uniformly.
Passing Signals Through Your API Layer
When you have a typed fetch wrapper, thread the signal parameter through every function that makes requests.
async function apiGet<T>(
url: string,
signal?: AbortSignal
): Promise<T> {
const response = await fetch(url, { signal });
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response.json() as T;
}The optional signal parameter lets callers opt into cancellation without changing the function signature for code that does not need it. When signal is provided, fetch uses it. When it is undefined, fetch behaves normally. This pattern makes cancellation available everywhere without forcing every caller to create a controller.
Common Mistakes
Not checking for AbortError. A cancelled request throws an AbortError, not a regular Error. If your catch block assumes all errors are Error instances, the AbortError will pass through unhandled. Always check error.name before assuming the error type.
Creating one AbortController for many requests. A single controller can only be aborted once. If you pass the same signal to multiple fetch calls, aborting it cancels all of them. This may be what you want, but if you need independent cancellation, create separate controllers.
Forgetting to call abort on unmount. If a component unmounts and an in-flight request later resolves and calls setState, React logs a warning. The abort pattern in the cleanup function prevents this entirely.
For handling errors after cancellation, see handle API errors in TypeScript. For retrying after a timeout, see retry failed API requests with TypeScript. For typing the fetch calls being cancelled, see build a typed fetch wrapper.
Rune AI
Key Insights
- Use AbortController to cancel in-flight fetch requests by passing its signal.
- The abort method triggers cancellation. The signal's aborted property checks state.
- A cancelled fetch rejects with an AbortError. Check error.name to detect it.
- Create AbortController in useEffect and abort in the cleanup function.
- Timeout wrappers can use AbortController to cancel slow requests automatically.
Frequently Asked Questions
What is AbortController in TypeScript?
How do I detect if a request was cancelled vs failed?
Should I abort requests when a React component unmounts?
Conclusion
Canceling async requests in TypeScript uses the AbortController API, which is fully typed in TypeScript's DOM library. Pass the signal to fetch, call abort when you need to cancel, and check for AbortError in your catch block. The type system helps you distinguish cancellation from real failures, so your error handling stays precise even when requests are interrupted.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.