Fetch API - Exercise 3
Pass an AbortSignal to fetch and call abort() when the request is no longer useful.
const controller = new AbortController();
fetch('/api/search', { signal: controller.signal });
controller.abort();Check for the AbortError name and avoid showing an error message for intentional cancellation.
try {
await fetch('/api/search', { signal: controller.signal });
} catch (error) {
if (error.name !== 'AbortError') showError(error);
}Abort the request from a timer and clear that timer after the request settles.
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
return await fetch(url, { signal: controller.signal });
} finally {
clearTimeout(timeout);
}Retry a bounded number of times and wait between attempts; do not retry every client or validation error.
for (let attempt = 1; attempt <= 3; attempt++) {
const response = await fetch(url);
if (response.ok) return response.json();
if (response.status < 500) break;
await new Promise(resolve => setTimeout(resolve, attempt * 500));
}Abort the previous controller before starting the next search and create a fresh controller for the new request.
let activeController;
async function search(query) {
activeController?.abort();
activeController = new AbortController();
return safeFetch(`/api/search?q=${encodeURIComponent(query)}`, {
signal: activeController.signal
});
}Process a queue in batches instead of starting every request at once.
for (let i = 0; i < urls.length; i += 3) {
const batch = urls.slice(i, i + 3);
await Promise.all(batch.map(url => safeFetch(url)));
}Treat server data as untrusted and verify the fields your UI needs before rendering.
function isUser(value) {
return value && typeof value.id === 'number' && typeof value.name === 'string';
}
if (!isUser(data)) throw new Error('Invalid user response');Use Promise.allSettled() when one failed request should not discard successful results.
const results = await Promise.allSettled([
safeFetch('/api/profile'),
safeFetch('/api/notifications')
]);
const successful = results.filter(result => result.status === 'fulfilled');