In this blog, I’ll walk you through the practical steps of cancelling a fetch request using JavaScript, with a focus on the AbortController API. By the end, you’ll have a clear understanding of how to make your web apps more responsive and resource-friendly.
Why Would You Need to Cancel a Fetch Request?
Cancelling fetch requests is crucial in scenarios where:
User Experience: When users navigate away from a page, there’s no need to continue fetching data for that page.
Search Optimization: In search features where each keystroke triggers a request, it’s more efficient to cancel the previous request before sending a new one.
Timeout Scenarios: In case of network delays or long-running requests, you might want to set a timeout and cancel the request if it exceeds a certain duration.
Understanding AbortController
The AbortController API provides an elegant way to cancel fetch requests. It works by creating an AbortController instance, whose signal is passed to the fetch request. If you call the abort() method on the controller, it cancels the request.
Step-by-Step Guide to Cancelling Fetch Requests
1. Basic Setup Using AbortController
Let’s start with the most basic example: creating an AbortController and cancelling a fetch request.
// Step 1: Create an instance of AbortController
const controller = new AbortController();
// Step 2: Pass the signal to the fetch request
fetch('https://jsonplaceholder.typicode.com/posts', { signal: controller.signal })
.then(response => response.json())
.then(data => console.log('Data:', data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch request was canceled');
} else {
console.error('Fetch error:', err);
}
});
// Step 3: Cancel the fetch request
controller.abort();
2. Practical Use Case: Cancelling Requests on User Interaction
One common scenario is cancelling a fetch request in response to user interaction. For instance, when implementing a search feature, each keystroke might trigger a new fetch request. Cancelling the previous request prevents outdated or irrelevant data from being processed.
let controller;
function search(query) {
// Cancel the previous request if it exists
if (controller) {
controller.abort();
}
// Create a new controller for the new request
controller = new AbortController();
// Fetch data with the new controller
fetch(`https://jsonplaceholder.typicode.com/posts?query=${query}`, { signal: controller.signal })
.then(response => response.json())
.then(data => console.log('Search results:', data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('Previous request canceled');
} else {
console.error('Fetch error:', err);
}
});
}
// Example usage: simulate user typing
search('React');
search('Vue'); // The request for 'React' is canceled
3. Implementing Timeout for Fetch Requests
Timeouts are essential when dealing with unreliable network conditions. Using AbortController, you can easily implement a timeout mechanism that cancels the fetch request if it takes too long.
function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
return fetch(url, { signal: controller.signal })
.then(response => {
clearTimeout(timeoutId);
return response.json();
})
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch request timed out');
} else {
console.error('Fetch error:', err);
}
});
}
// Example usage
fetchWithTimeout('https://jsonplaceholder.typicode.com/posts', 3000)
.then(data => console.log('Data:', data));
Handling Fetch Request Cancellation Gracefully
When cancelling fetch requests, it’s important to handle them gracefully. This involves distinguishing between errors caused by cancellations and other types of errors.
fetch(url, { signal: controller.signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
// Handle cancellation specifically
console.log('Request was canceled');
} else {
// Handle other types of errors
console.error('Request failed', err);
}
});