Request Cancellation: Cancel Fetch Requests
Learn: Request Cancellation: Cancel Fetch Requests
Welcome to TopperBlog! 👋
I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.
🎯 What I Write About:
• AI/ML Engineering & LLMs
• Web3 & Blockchain Development
• System Design & Architecture
• Interview Preparation (FAANG)
• Freelancing & Remote Work
• Modern Tech Stacks (Next.js, React, Rust, TypeScript)
• Performance Optimization & Best Practices
💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.
📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.
🌐 Let's connect and grow together in this amazing tech journey!
#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering
Request Cancellation: Cancel Fetch Requests with AbortController
Problem
When making HTTP requests with the Fetch API, you may need to cancel them before completion. Common scenarios include:
- User navigates away from a page
- User clicks a "Cancel" button
- Request takes too long (timeout)
- New request supersedes a previous one
- Component unmounts in React
Without cancellation, these requests continue consuming bandwidth and resources, potentially causing memory leaks or race conditions.
Solution
The AbortController API provides a standard way to cancel fetch requests. It allows you to signal one or more fetch operations to abort, stopping them mid-flight.
How It Works
- Create an
AbortControllerinstance - Pass its
signalto the fetch request - Call
abort()to cancel the request - Handle the
AbortErrorexception
Code Examples
Basic Cancellation
// Create controller
const controller = new AbortController();
// Make request with signal
fetch('https://api.example.com/data', {
signal: controller.signal
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Request was cancelled');
} else {
console.error('Request failed:', error);
}
});
// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);
Timeout Implementation
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(error => {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error(`Request timeout after ${timeout}ms`);
}
throw error;
});
}
// Usage
fetchWithTimeout('https://api.example.com/data', 3000)
.then(data => console.log(data))
.catch(error => console.error(error.message));
React Component with Cleanup
import { useEffect, useRef, useState } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const controllerRef = useRef(null);
useEffect(() => {
// Create new controller for this effect
controllerRef.current = new AbortController();
const controller = controllerRef.current;
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch('https://api.example.com/data', {
signal: controller.signal
});
if (!response.ok) throw new Error('API error');
const result = await response.json();
// Only update state if request wasn't aborted
if (!controller.signal.aborted) {
setData(result);
}
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
if (!controller.signal.aborted) {
setLoading(false);
}
}
};
fetchData();
// Cleanup: abort request on unmount
return () => controller.abort();
}, []);
return (
<div>
{loading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}
Multiple Requests with Single Controller
const controller = new AbortController();
// Multiple requests share same signal
Promise.all([
fetch('https://api.example.com/users', { signal: controller.signal }),
fetch('https://api.example.com/posts', { signal: controller.signal }),
fetch('https://api.example.com/comments', { signal: controller.signal })
])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(data => console.log('All data:', data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('All requests cancelled');
}
});
// Cancel all at once
setTimeout(() => controller.abort(), 3000);
Search with Request Deduplication
class SearchManager {
constructor() {
this.controller = null;
}
async search(query) {
// Cancel previous request
if (this.controller) {
this.controller.abort();
}
// Create new controller
this.controller = new AbortController();
try {
const response = await fetch(
`https://api.example.com/search?q=${encodeURIComponent(query)}`,
{ signal: this.controller.signal }
);
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('Search cancelled');
return null;
}
throw error;
}
}
}
// Usage
const searcher = new SearchManager();
// Only latest search completes
searcher.search('javascript');
searcher.search('python'); // Cancels previous
searcher.search('rust'); // Cancels previous
With Async/Await and Error Handling
async function robustFetch(url, options = {}) {
const controller = new AbortController();
const timeout = options.timeout || 10000;
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error(`Request timeout after ${timeout}ms`);
}
throw error;
} finally {
clearTimeout(timeoutId);
}
}
// Usage
try {
const data = await robustFetch('https://api.example.com/data', {
timeout: 5000
});
console.log(data);
} catch (error) {
console.error('Fetch failed:', error.message);
}
Tips & Best Practices
✅ Do's
Always handle AbortError separately
.catch(error => { if (error.name === 'AbortError') { // Handle cancellation } else { // Handle other errors } });Check signal.aborted before state updates
if (!controller.signal.aborted) { setState(data); }Clean up in React useEffect
return () => controller.abort();Reuse controllers for related requests
// Cancel all related requests at once Promise.all([fetch(url1, {signal}), fetch(url2, {signal})]); controller.abort();Combine with timeout logic
setTimeout(() => controller.abort(), timeout);
❌ Don'ts
Don't ignore AbortError in logging
// Bad .catch(error => console.error(error)); // Logs AbortError // Good .catch(error => { if (error.name !== 'AbortError') { console.error(error); } });Don't forget to clear timeouts
// Bad setTimeout(() => controller.abort(), 5000); // Good const id = setTimeout(() => controller.abort(), 5000); // ... later clearTimeout(id);Don't reuse aborted controllers
// Bad controller.abort(); fetch(url, { signal: controller.signal }); // Won't work // Good controller.abort(); const newController = new AbortController(); fetch(url, { signal: newController.signal });
Browser Support
- ✅ All modern browsers (Chrome 66+, Firefox 57+, Safari 11.1+, Edge 16+)
- ✅ Node.js 15+
- ⚠️ IE 11 not supported
Performance Considerations
- Minimal overhead: AbortController is lightweight
- Memory efficient: Aborted requests release resources immediately
- No memory leaks: Proper cleanup prevents dangling requests
- Race condition prevention: Abort old requests before new ones
Summary
| Feature | Benefit |
| AbortController | Standard API for cancelling requests |
| signal property | Passed to fetch to enable cancellation |
| abort() method | Triggers cancellation immediately |
| AbortError | Thrown when request is cancelled |
| Cleanup pattern | Prevents memory leaks in React |
| Timeout integration | Automatic request cancellation after delay |
Use AbortController to build responsive, resource-efficient applications that handle user interactions gracefully and prevent unnecessary network traffic.