Promise rejection
An async failure that must be handled.
Async debugging is about following work that completes later: promises, timers, fetch calls and event-driven code.
async function loadData() {
try {
const response = await fetch("/api/records");
return await response.json();
} catch (error) {
console.error(error);
}
}
Errors & Debugging
Async code does not fail on the same timeline as synchronous code. A promise rejection happens later, so it needs await with try/catch or a .catch handler.
Fetch adds another trap: HTTP error statuses do not reject by themselves. You must check response.ok.
Async debugging often means tracking where a promise was created, where it was awaited and where its rejection was handled.
An async failure that must be handled.
The cleanest way to catch errors in async functions.
HTTP status check for fetch responses.
A promise failed without a handler.
Examples
async function loadJson(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
function run() {
loadData();
}
// If loadData rejects, this function does not handle it.
Code patterns
These examples focus on the debugging patterns you will reuse: safe parsing, throwing, custom error types, console inspection, breakpoints and async failure handling.
Catch async failures in one readable block.
async function run() {
try {
const data = await loadData();
return data;
} catch (error) {
return null;
}
}
Attach a handler when not using await.
loadData().catch((error) => {
console.error(error);
});
Throw on bad HTTP status deliberately.
const response = await fetch("/api/records");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
Clear loading state after success or failure.
loading = true;
try { await loadData(); }
finally { loading = false; }
Rules that matter
Debugging starts before something breaks. Clear error types, helpful messages and visible failure states make the code easier to repair.
Otherwise errors can escape the visible flow.
It reads like synchronous error handling.
Fetch does not reject for HTTP error statuses.
Cleanup should happen on success and failure.
Deep .then chains are harder to debug.
Unexpected async errors should reach monitoring or logs.
Production thinking
Async failures are easy to miss because they happen later. Clear handling keeps later work visible.
Loading and error states should be announced or shown clearly so users are not left waiting silently.
Production async code needs timeout, abort, status handling and rejection reporting.
Content that appears only after async work may not be reliable for SEO unless rendered or hydrated carefully.
Live code lab
The preview runs inside an isolated iframe. The JavaScript is placed inside the HTML editor for now, so every example stays together and remains easy to understand.
Mini assignment
Practice assignment
Try it yourself
Self-check
A JavaScript feature is not production-ready until you know how it fails and how you will inspect that failure.
Chapter checkpoint
Finish this chapter by turning the lessons into a small practical proof.
Build a small example that combines three lessons from this chapter.
Can you explain the important tradeoff without reading from the page?