Asynchronous JavaScript
Abort Controller & Cancelling Async Operations
Master cancelling asynchronous operations in JavaScript. Learn how to abort fetch requests, remove event listeners dynamically, and build cancellable promises.
1. Introduction
Historically, once a Promise or fetch request was initiated, it was impossible to cancel it. ES6 introduced the AbortController class as a standard API to cancel asynchronous operations dynamically.
2. Why It Matters
In single-page applications, users often navigate away from pages before search requests or image downloads complete. Cancelling these pending requests saves server resources and bandwidth, preventing slow network performance.
3. Real-World Analogy
Think of a Food Delivery Order Cancel Button:
- Uncancellable Promise: You place an order. Halfway through, you realize you ordered from the wrong restaurant. There is no cancellation button. The kitchen prepares the food, the driver drives to your house, and you are charged, even if you throw the food away.
- AbortController (Cancel Request): You click "Cancel Order" on the app. The app sends a signal (Abort Signal) to the restaurant. The kitchen stops cooking immediately, the driver is redirected, and the transaction cancels, saving resources.
4. How It Works
The cancellation mechanism consists of two parts:
1. AbortController: The controller object that manages the operation. It exposes an abort() method to trigger the cancellation.
2. AbortSignal: The read-only signal property passed to asynchronous functions (like the fetch API). It allows them to monitor the abort status.
5. Architectural Uses of AbortController
AbortController is not limited to fetch requests:
• Event Listener Removal: You can pass an abort signal to addEventListener option parameter, allowing you to remove a group of listeners at once by calling abort().
• Cancellable Promises: You can check the signal.aborted property inside custom Promises to reject them early if aborted.
6. Practical Example
This script demonstrates implementing a request timeout wrapper using AbortController:
7. Common Mistakes
- Not checking for AbortError in catch blocks: Treat aborted requests differently than real failures (like network or parsing errors) to prevent showing false error alerts to users.
- Reusing the same controller: Once
abort()is called, the signal state remains aborted. Create a new controller instance for subsequent requests.
8. Quick Quiz
Q1: What error name is thrown when a fetch request is aborted?
A) CancelError
B) AbortError
Answer: B — Aborting a fetch request causes it to reject with a DOMException named "AbortError".
9. Scenario-Based Challenge
The Search Auto-Suggest Cancellation:
As a user types in a search box, requests are sent to the server. If a new request is sent before the previous one completes, cancel the old request to prevent out-of-order responses. Write a wrapper using AbortController to implement this cancellation logic.
10. Debugging Exercise
Identify why this component class fails on its second fetch run:
class DataFetcher { controller = new AbortController();async load(url) { // Bug: Reusing the same controller without resetting it! return fetch(url, { signal: this.controller.signal }).then(r => r.json()); }
cancel() { this.controller.abort(); } } const api = new DataFetcher(); api.load('/data'); api.cancel(); // Cancel first load api.load('/data'); // rejects immediately with AbortError! Why?
View Solution
Diagnosis: Calling abort() marks the controller's signal as aborted permanently. Reusing the same controller instance for a new request causes the new request to reject with an AbortError immediately.
Fix: Create a new AbortController instance for every load request:
class DataFetcher { controller = null;
async load(url) { if (this.controller) this.controller.abort(); // Cancel previous load if still running this.controller = new AbortController(); return fetch(url, { signal: this.controller.signal }).then(r => r.json()); } }
11. Interview Questions
🟢 Q1: Explain how AbortController is used to cancel fetch requests, event listeners, and custom Promises.
Answer:
• Fetch Requests: Pass the controller.signal as an option to the fetch() call. Calling controller.abort() cancels the network request, causing the fetch Promise to reject with an AbortError.
• Event Listeners: Pass the signal inside the options object when calling addEventListener. Calling abort() automatically removes the event listener.
• Custom Promises: Inside the Promise executor, register an event listener on the signal: signal.addEventListener('abort', () => reject(new AbortError())). If aborted, reject the promise explicitly and clean up any active timers or connections.
12. Production Considerations
- • Resource Cleanup: In React applications, use AbortController inside
useEffecthooks to cancel pending network requests if the component unmounts before the request completes, preventing memory leaks and state updates on unmounted components.