SKU // 115

JavaScript Debugging Async

Most code runs top to bottom, one line after another. Asynchronous (async) code does not: it starts something slow, like a timer or loading data from a server, and runs the rest later, when that finishes. That makes its bugs feel invisible, because nothing seems to happen, or it happens in a strange order. This last lesson explains just enough about async code to debug it.

// Async Code Runs Later

You met this in JS Timers. A setTimeout callback waits, even with a delay of 0, until the rest of the script has finished:

console.log("A");
setTimeout(() => console.log("B"), 0);
console.log("C");
A C B

That is the first rule of async debugging: the order the code is written is not the order it runs.

// Promises in One Minute

A Promise is an object that stands for a value that will arrive later. You attach what to do with the value using .then(callback). This little helper returns a promise that finishes after some milliseconds:

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

console.log("start");
wait(100).then(() => console.log("100 ms later"));
console.log("end of script");
start end of script 100 ms later

The course does not cover promises in depth, but you will meet them constantly: loading data, timers, and many browser features all return promises.

// async and await

await, used inside a function marked async, pauses that function until a promise finishes, so async code reads top to bottom again. The rest of the program keeps running in the meantime:

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

async function run() {
  console.log("waiting...");
  await wait(100);
  console.log("done");
}

run();
console.log("run() returned right away");
waiting... run() returned right away done
Tip
You can set a breakpoint on an await line and step through async code just like normal code. The debugger waits for the promise before moving to the next line.

// Debugging fetch()

fetch(url) asks a server for a file and returns a promise of a response. Reading the response body is a second async step, response.json(), which turns JSON text into a JavaScript value. The lesson pages have a small data.json file next to them, so you can try this in the console right here:

fetch("data.json")
  .then(response => response.json())
  .then(data => console.log(data));
{name: "Ava", scores: [92, 85, 78]}

If nothing appears, log the response itself before using it. Its status and ok properties tell you whether the server actually sent the file:

fetch("data.json")
  .then(response => {
    console.log(response.status, response.ok);
    return response.json();
  })
  .then(data => console.log(data.name));
200 true Ava

// Check the Network Tab

Many async bugs are really network problems. The developer tools' Network tab lists every request the page makes. Reload the page with it open and check:

// Handling Async Errors

Inside an async function, wrap the await lines in try...catch (JS Error Statements). Here W3Schools' example asks for a file that does not exist:

async function loadData() {
  try {
    const response = await fetch("wrong.json");
    console.log(response.status, response.ok);
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log(error.name + ": " + error.message);
  }
}

loadData();
404 false SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Trap: fetch does not fail on a 404
fetch only rejects when the request cannot be made at all (no network, blocked address). A "404 Not Found" is still a response, so the error above does not come from the missing file: it comes from response.json() trying to read the server's HTML error page as JSON. That is why the message mentions <!DOCTYPE. Check response.ok and throw a clear error yourself.
async function loadData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`Could not load ${url}: HTTP ${response.status}`);
    }
    const data = await response.json();
    console.log(data.name);
  } catch (error) {
    console.log(error.message);
  }
}

async function main() {
  await loadData("data.json");
  await loadData("wrong.json");
}
main();
Ava Could not load wrong.json: HTTP 404

main awaits each call so they run one after the other. Without the awaits, both requests would run at the same time and log in whichever order they happened to finish.

With a .then() chain instead of await, add a .catch() at the end to handle an error from any step:

fetch("wrong.json")
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.log("caught:", error.name));
caught: SyntaxError

// Unhandled Errors in Async Code

An error thrown in async code that nothing catches does not stop your other code, but it is not invisible either. The console shows it with the words "in promise":

async function fail() {
  throw new Error("boom");
}

fail();
console.log("the rest of the script still runs");
the rest of the script still runs Uncaught (in promise) Error: boom
W3Schools errors on its Debugging Async page
  • It says async errors must be handled because "Otherwise they fail silently." They are not silent: the console reports every unhandled one as Uncaught (in promise). What is true is that the page shows nothing, so a user never finds out. Keep the console open, and handle errors where the user needs to know.
  • Its "Promises That Never Resolve" example (a function that forgets to return its fetch) is not a promise that never resolves. The fetch still finishes; the caller just receives undefined instead of the promise, so it has nothing to wait for. See below.

// The Missing return

A function that starts async work must return the promise, or its caller cannot wait for the result:

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

function getNumber() {
  wait(10).then(() => 42);            // forgot return
}
function getNumberFixed() {
  return wait(10).then(() => 42);
}

console.log(getNumber());
getNumberFixed().then(n => console.log(n));
undefined 42

If await getNumber() gives you undefined, look for a missing return in the function first.

// Async Debugging Checklist

  1. Check the console for errors, including "Uncaught (in promise)"
  2. Check the Network tab: status, path, and response
  3. Log the response (status, ok) before using its data
  4. Use try...catch around await, or .catch() at the end of a chain
  5. Make sure every function that starts async work returns its promise
  6. Set breakpoints on await lines and step through
You Finished the Course
That is the last lesson. You can now write JavaScript, and, just as important, find out why it does not work. Debugging is not a talent; it is a habit. Read the error, check the facts, and fix one thing at a time.

// Quick Check

What does this log, in order?

console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
Show answer
1, 4, 3, 2. The normal lines run first. Then JavaScript runs promise callbacks before timer callbacks, even a timer with a delay of 0.

fetch("scores.json") gets a 404. What happens?

  1. fetch throws a TypeError immediately
  2. The promise rejects with "Not Found"
  3. You get a response with ok set to false and status 404
  4. Nothing: the promise never finishes
Show answer
C. A 404 is still a response. fetch only rejects when the request cannot be made at all, so you must check response.ok yourself.

const data = await loadScores(); gives undefined, but the Network tab shows the file loaded fine. What should you check first?

Show answer
That loadScores returns its promise (for example, return fetch(...).then(...)). Without return, the function gives back undefined right away, and await has nothing to wait for.

// Try It

  1. Click Open Playground. Fetch data.json and log data.scores
  2. Open the first example with its Try it Yourself button, predict the order of the three lines, then run it
  3. Fetch a file that does not exist and handle it with response.ok. Then open your browser's developer tools (F12) and watch the request in the Network tab
  4. Write an async function that waits 1 second, then logs a message, and set a breakpoint on its await line
// Video Walkthroughs
Last updated: Oct 10, 2026, 1:50 AM ET