🚫 Cancel Requests Before They Complete
User types fast, sends 10 search requests. All 10 return, UI flickers. AbortController cancels outdated requests.
The Problem
// ❌ Search on every keystroke - race condition!
searchInput.addEventListener('input', async (e) => {
const results = await fetch(`/api/search?q=${e.target.value}`);
const data = await results.json();
displayResults(data);
});
// User types: "javascript"
// Sends: /api/search?q=j
// /api/search?q=ja
// /api/search?q=jav
// /api/search?q=java
// /api/search?q=javas
// ... 10 requests
// All 10 responses come back
// Results flicker as each arrives
// Last one might not be newest!
AbortController Solution
// ✅ Cancel previous request on new keystroke
let controller = null;
searchInput.addEventListener('input', async (e) => {
// Cancel previous request if exists
if (controller) {
controller.abort();
}
// Create new controller
controller = new AbortController();
try {
const results = await fetch(`/api/search?q=${e.target.value}`, {
signal: controller.signal
});
const data = await results.json();
displayResults(data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request cancelled');
} else {
console.error('Request failed:', error);
}
}
});
// Only latest request completes
// Previous ones cancelled immediately
🎯 How It Works
- Create controller:
new AbortController() - Pass signal to fetch:
fetch(url, { signal: controller.signal }) - Cancel when needed:
controller.abort() - Catch AbortError: Handle cancellation gracefully
React useEffect Example
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function fetchUser() {
try {
const response = await fetch(`/api/users/${userId}`, {
signal: controller.signal
});
const data = await response.json();
setUser(data);
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Fetch failed:', error);
}
}
}
fetchUser();
// Cleanup: Cancel request if component unmounts
// or userId changes before request completes
return () => controller.abort();
}, [userId]);
return {user?.name};
}
Timeout with AbortController
⏱️ Auto-cancel after timeout
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
// Auto-abort after timeout
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
signal: controller.signal
});
clearTimeout(timeoutId);
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
// Usage
try {
const data = await fetchWithTimeout('/api/slow', 3000);
} catch (error) {
console.error('Request timed out or failed');
}
Cancel Multiple Requests
// One controller for multiple requests
const controller = new AbortController();
Promise.all([
fetch('/api/users', { signal: controller.signal }),
fetch('/api/posts', { signal: controller.signal }),
fetch('/api/comments', { signal: controller.signal })
])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(([users, posts, comments]) => {
console.log('All loaded');
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('All requests cancelled');
}
});
// Cancel all 3 requests at once
document.getElementById('cancel').addEventListener('click', () => {
controller.abort();
});
✅ Use Cases
- Search autocomplete: Cancel outdated searches
- React useEffect: Cancel on unmount/dependency change
- Pagination: Cancel previous page when user clicks next
- File uploads: User cancels upload
- Timeouts: Abort slow requests automatically
💡 Important Notes
- Browser support: All modern browsers (IE11 needs polyfill)
- Server still processes: AbortController cancels client-side, server may still complete
- Always catch AbortError: Don’t treat as real error
- One controller = one abort: Can’t reuse after abort, create new one
“Search was sending 20+ requests per search. Added AbortController. Now only final request completes. Reduced server load 95%. UI stopped flickering. Users love the smooth experience.”
