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');