On this page · sections
JavaScript Error Handling & Cancellation
Can you fail with a useful error, stop waiting, and actually cancel the underlying work?
try/catch is not a strategy. Interviews want custom errors, Promise rejection rules, timeouts, and AbortController.
Timeout wrappers and retry sit on top of a scheduler in Task Scheduler. This page is the language and host contract.
Questions
Errors
Cancellation
Senior-Level Follow-ups
Errors
1. Throw, Reject, and Custom Errors
Why This Matters
throw "string" loses stack traces. Swallowing errors in catch hides production failures.
Problem
How should errors propagate in sync code and in Promises?
Example
class AppError extends Error {
constructor(code, message) {
super(message);
this.name = "AppError";
this.code = code;
}
}
throw new AppError("UNAVAILABLE", "payments down");Sync throw unwinds the stack until catch.
Async:
async function load() {
throw new AppError("NETWORK", "offline");
}
load().catch((err) => {
if (err instanceof AppError) return show(err);
throw err;
});Throwing inside then rejects the next Promise. try/catch around await is the same.
Interview Answer
“I throw
Errorsubclasses with a name and code. Sync errors unwind; async errors reject. I catch at a boundary, log, and rethrow unknown errors. I never throw strings.”
2. fetch Does Not Reject on HTTP Errors
Why This Matters
retry(fetch) will not retry a 500 unless you throw.
Problem
When does fetch reject, and how do you turn HTTP failure into an exception?
Example
const response = await fetch("/api/users");
if (!response.ok) {
throw new AppError("HTTP", `HTTP ${response.status}`);
}
return response.json();fetch rejects on network failure or abort — not on 404/500.
response.json() can still throw on invalid JSON.
Interview Answer
“
fetchfulfills with a Response for HTTP errors. I checkok(or status) and throw. That keeps retry and UI error paths uniform.”
Cancellation
3. Timeout vs Abort
Why This Matters
Promise.race against a timer only stops waiting.
Problem
What is the difference between a timeout wrapper and cancelling fetch?
Example
Timeout → stop waiting; work may continue
Abort → tell the host to stop the operationPromise.race([
fetch("/api"),
sleep(3000).then(() => {
throw new Error("timeout");
}),
]);The request can still complete and invoke handlers unless you abort.
Always clearTimeout when the operation wins, or the timer leaks a rejection.
Interview Answer
“A timeout is a competing Promise. Cancellation is a signal to the operation. For
fetch, I passAbortSignaland abort on timeout. Race alone is not enough.”
4. AbortController and Signals
Why This Matters
One signal can cancel fetch, streams, and event listeners together.
Problem
How do you wire abort through an async function?
Example
const controller = new AbortController();
const response = await fetch("/api/data", {
signal: controller.signal,
});
controller.abort();Aborted fetch rejects with AbortError (DOMException). Callers should treat abort as control flow, not as a toast-worthy failure.
el.addEventListener("click", onClick, { signal: controller.signal });Nested work should accept signal and forward it. Do not create a new controller per child unless you need independent cancel.
Interview Answer
“I create an
AbortController, passsignalintofetchand listeners, and abort from timeout, unmount, or a new request.AbortErroris expected. I forward the same signal through the call chain.”
Senior-Level Follow-ups
5. Ignoring Late Resolves
A cancelled request may still resolve. Guard UI updates:
let generation = 0;
async function search(q) {
const id = ++generation;
const data = await fetchResults(q);
if (id !== generation) return;
setResults(data);
}Or abort the previous controller so the network work stops too.
6. useEffect Cleanup
useEffect(() => {
const controller = new AbortController();
load(controller.signal);
return () => controller.abort();
}, [id]);Cleanup must abort. Otherwise Strict Mode double-mount and route changes leak requests and setState on unmounted trees.
7. What Should Be Retryable?
Retry transient failures: network, 429, 503.
Do not retry 400, 401, 403, or AbortError.
Retries are sequential with delay. Parallel retries amplify load. Details: Task Scheduler.
Final Interview Strategy
1. Throw Error subclasses
2. Catch at boundaries
3. fetch needs an ok check
4. Timeout ≠ cancel
5. AbortSignal is the cancel token
6. Ignore stale responses
7. Abort on unmount; don't retry AbortErrorSenior-level signal: Separate “the Promise settled” from “the operation stopped.” Production async code needs both.