Volume 1

Q019: Fetch, AbortController and Request Lifecycle

Difficulty: SeniorFrequency: HighAnswer time: 7-10 minutes

What Interviewers Want To Evaluate

This question tests whether you can reason about network requests as application lifecycle events. Interviewers want the Fetch API, promises, response handling, cancellation, timeouts, retries, stale responses, component cleanup, and user experience under slow or unreliable networks.

For senior roles, the strongest answer connects request code to correctness: duplicate submissions, race conditions, aborting stale work, and preserving user trust.

Short Interview Answer

fetch starts an HTTP request and returns a promise that resolves to a Response when headers are available. It rejects for network failures, but not for HTTP status codes like 404 or 500, so callers must check response.ok. AbortController lets the app cancel a request, usually during component cleanup, route changes, search input changes, or timeouts. Senior request handling also needs stale-result protection, retry policy, loading states, and error classification.

Detailed Interview Answer

Fetch is low-level. It gives you request and response primitives, but it does not automatically handle JSON errors, retries, timeouts, or cancellation semantics for your product.

The lifecycle starts when code creates a request. The browser handles DNS, connection reuse, TLS, cache checks, CORS, request upload, response headers, and response body streaming. The promise resolves once the response headers are available. Reading the body is a separate step, such as response.json().

AbortController provides a signal. Passing signal to fetch allows cancellation. When aborted, fetch rejects with an abort error. The app should treat abort differently from a real failure shown to the user.

Deep Technical Explanation

The usual lifecycle is:

create controller
start fetch with signal
headers arrive
read body
parse body
commit result if still current
cleanup or abort when stale

The "commit if current" step is important. A request can finish after the user has already typed a newer query or navigated away.

Code Example

async function loadUser(userId: string, signal: AbortSignal) {
  const response = await fetch(`/api/users/${userId}`, { signal });

  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }

  return response.json();
}

In UI code, create a controller per request and abort it when the request becomes stale.

const controller = new AbortController();

loadUser("42", controller.signal)
  .then((user) => console.log(user))
  .catch((error) => {
    if (error.name !== "AbortError") {
      console.error(error);
    }
  });

controller.abort();

Internal Working

fetch resolves on HTTP responses even when status indicates application failure. This lets callers inspect headers and body for error details. Network failures, blocked CORS responses, and aborts reject the promise.

Response bodies are streams. Once a body is read, it cannot be read again unless cloned before consumption.

Architecture Discussion

Request code should be centralized enough to enforce defaults: credentials mode, headers, JSON parsing, error classification, tracing headers, retry policy, and timeout behavior.

Do not hide every detail behind one magical function. Some calls need streaming, uploads, idempotency keys, optimistic updates, or stricter cancellation.

Timeouts and Retries

Fetch does not include a built-in timeout option. You can implement one with AbortController and a timer. Retries should be limited and usually reserved for idempotent requests or operations with idempotency keys.

Retrying a payment, signup, or mutation without idempotency can duplicate work.

Common Mistakes

A common mistake is assuming fetch rejects on 500. It does not. You must check response.ok.

Another mistake is not cancelling or ignoring stale requests in search and autocomplete flows. Old results can overwrite newer results.

Real Production Story

An autocomplete can show the wrong options when the user types quickly. The request for "rea" may finish after "react" and overwrite the UI. The fix is aborting stale requests or tracking request IDs before committing results.

Enterprise Example

In an admin dashboard, filters may trigger expensive report queries. Changing filters should cancel or ignore old requests, show clear loading state, and avoid retrying non-idempotent export jobs.

Performance Discussion

Request lifecycle affects INP and perceived speed. Avoid blocking input on request completion. Use optimistic UI where safe, cache repeated reads, debounce search, and stream large responses when appropriate.

Security Considerations

Do not put secrets in browser fetch calls. Be careful with credentials, CORS, CSRF, and error messages. Avoid logging sensitive response bodies to the console.

Senior Engineer Perspective

A senior engineer should discuss user states, not only syntax. Loading, success, empty, error, offline, stale, retrying, and cancelled states need distinct handling.

Lead Engineer Perspective

A lead should define request conventions: API client shape, cancellation rules, retry policy, error taxonomy, observability, and idempotency expectations.

Whiteboard Explanation

Draw a user typing into search, several overlapping requests, and only the newest request being allowed to update UI. Then show AbortController cancelling old requests.

Revision Notes

Fetch resolves for HTTP errors and rejects for network, CORS, and abort failures. Always check response.ok. Use AbortController for cancellation and stale request cleanup.

Key Takeaways

Good request handling is about lifecycle and correctness. The syntax is simple; the hard part is preventing stale, duplicated, or misleading UI.

Related Topics

  • CORS
  • HTTP caching
  • Event loop
  • Promises
  • React effects
  • Optimistic UI

Practice Exercise

Build a search box that cancels stale requests, ignores old responses, handles empty results, and distinguishes aborts from real errors.

Follow-Up Questions

  1. When does a fetch promise resolve?
  2. Does fetch reject on 404?
  3. What does response.ok mean?
  4. How does AbortController work?
  5. How would you implement a timeout?
  6. When are retries dangerous?
  7. How do you prevent stale search results?
  8. Why can a response body only be read once?
  9. How does CORS failure appear to fetch?
  10. What request states should a UI represent?

Things Interviewers Hate Hearing

"Just use fetch and catch errors" misses the main problem. HTTP errors, aborts, stale responses, and product failures need different handling.

How I Would Answer This In A Real Interview

I would say fetch returns a promise for the response, but it does not reject on HTTP error statuses, so I always check response.ok. Then I would explain that body parsing is separate and that cancellation uses AbortController.

For a senior answer, I would move quickly into lifecycle: route changes, autocomplete, duplicate submissions, retries, and stale response protection. The goal is to keep the UI correct when the network is slow, fast, duplicated, or cancelled.