JS AsyncLesson #247

Asynchronous Debugging

Asynchronous code can be more difficult to debug than synchronous code.

Asynchronous Bugs

Asynchronous code can be more difficult to debug than synchronous code.

The code often executes later than expected, and several asynchronous operations may run at the same time.

This chapter shows techniques for finding and fixing common problems in asynchronous JavaScript.

This chapter shows practical ways to debug fetch(), promises, and async/await.

Async debugging is about finding where the code stops. > Then to find why it stopped.

Log Events

The easiest way to debug asynchronous code is to log when things happen.

Use console.log()

Example Code

javascript
Try it Yourself
console.log("Before fetch");

const promise = fetch("fetch.txt");

console.log("After fetch");

Use a display function

Example Code

javascript
Try it Yourself
myDisplayer("Before fetch");

const promise = fetch("fetch.txt");

myDisplayer("After fetch");
Note > console.log(), often display the current state of a Promise like: >
> > This information is provided by the debugging tool. It is not part of the JavaScript language. Why can't JavaScript do the same?

There is no standard properties like:

  • promise.state
  • promise.status

They simply do not exist.

The state part of the Promise is not in its public API.


Async Bugs Can be Invisible

Async code often fails after your function has already returned.

This makes it feel like nothing happened.

Example

Example Code

javascript
Try it Yourself
// Async function to download a file

async function loadText(file) {

  const response = await fetch(file);

  myDisplayer(response);

}

myDisplayer("Start");

// Try to load a missing file

loadText("missing.txt");

myDisplayer("Done");
The Done message prints even if loadText() fails. > This is because the fetch() fails later.

Rule 1: Always Handle Errors

Unhandled promise rejections confuse beginners.

Handle errors early and clearly.

Use try...catch to handle Promises.

Example

Example Code

javascript
Try it Yourself
// Async function to download a file

async function loadData(file) {

  try {

    let response = await fetch(file);

    let data = await response.json()

    myDisplayer(JSON.stringify(data));

  } catch (error) {

    myDisplayer(error);

  }

}

// Call async function

loadData("customer.json")
Note > The code above catches network errors and JSON parsing errors. > It does not catch HTTP errors like 404 (file not found).

Rule 2: Check response.ok

Fetch does not reject on HTTP errors like 404.

You must check response.ok.

Example

Example Code

javascript
Try it Yourself
// Async function to download a file

async function loadData(file) {

  try {

    let response = await fetch(file);

    if (!response.ok) {

      console.log("HTTP Error:", response.status);

      return;

    }

    let data = await response.json()

    myDisplayer(JSON.stringify(data));

  } catch (error) {

    myDisplayer(error);

  }

}
The code above separates HTTP errors from network errors. > Most fetch bugs are not JavaScript bugs. > They are URL and server response bugs.

Rule 3: Log the Right Things

Logging a promise is not the same as logging the data.

Log the response and status first.

Example

Example Code

javascript
Try it Yourself
// Async function to download a file

async function loadData(file) {

  let response = await fetch(file);

  myDisplayer(response.status);

  myDisplayer(response.headers.get("content-type"));

}
The above code tells you if the server returned what you expected.

Common Response Headers

HeaderDescription
Content-TypeType of the response (text/html, application/json, etc.)
Content-LengthSize of the response in bytes
Cache-ControlHow the response may be cached
ExpiresWhen the cached response expires
Last-ModifiedWhen the resource was last modified
ETagResource version identifier used for caching
DateTime the server generated the response
ServerInformation about the web server
Access-Control-Allow-OriginCORS policy
Content-EncodingCompression used (gzip, br, etc.)

Use the Browser Network Tab

The Network tab is a panel in your Browser's Developer Tools.

It shows all network request made by your web page.

It is one of the most useful tools for debugging asynchronous JavaScript because it lets you see exactly > what is happening when your code uses fetch(), loads images, downloads JSON, or sends data to a server.

How to Use the Developer Tools:

  • Open a Website
  • Press F12, or
  • Right-click the page and select Inspect.
  • Then select the Network tab.
  • Refresh the page

The Network tab shows every request made by your application:

  • Which files were requested
  • Whether they succeeded
  • HTTP status codes
  • Download times

Example

Example Code

javascript
Try it Yourself
const response = await fetch("customer.json");

Some Possible Results

NameStatusTypeSizeTime
customer.json200fetch548 Bytes134ms
customer.json404fetch
customer.jsonFailed
This is much easier than trying to guess why fetch() failed.

Use Breakpoints

Modern browsers allow you to pause JavaScript execution.

While execution is paused, you can inspect variables, Promises, and the Call Stack.


Example

Example Code

javascript
Try it Yourself
Example
const response = await fetch("demo.txt");

debugger;

const text = await response.text();
When execution reaches debugger, DevTools pauses automatically.

Breakpoints on await

You can set breakpoints on await lines.

This lets you inspect values before and after async steps.

Example

Example Code

javascript
Try it Yourself
async function loadData() {

  let response = await fetch("data.json");

  let data = await response.json();

  console.log(data);

}

Set a breakpoint on the first await line.

Step over and inspect response.


Common Async Errors

Forgetting await is the most common beginner mistake.

Example

Example Code

javascript
Try it Yourself
async function loadData() {

  let response = await fetch("data.json");

  let data = response.json();

  console.log(data);

}

This logs a promise, not the JSON.

Example

Example Code

javascript
Try it Yourself
async function loadData() {

  let response = await fetch("data.json");

  let data = await response.json();

  console.log(data);

}

Debugging Promise Chains

Promises can fail anywhere in the chain.

Add logs between steps to find where it fails.

Example

Example Code

javascript
Try it Yourself
fetch("data.json")

.then(function(response) {

  console.log("Got response");

  return response.json();

})

.then(function(data) {

  console.log("Got data");

  console.log(data);

})

.catch(function(error) {

  console.log("Failed");

  console.log(error);

});

This shows which step is failing.


Understand Execution Order

Example

Example Code

javascript
Try it Yourself
myDisplayer("Start");

setTimeout(function() {myDisplayer("Timer")},1000);

myDisplayer("End");

Debug Parallel Operations

Result

Example Code

javascript
Try it Yourself
const customer = fetch("customer.json");

const products = fetch("products.json");

const news = fetch("news.json");
  • All three requests start immediately
  • The browser may finish them in any order
  • That is normal

Check response.ok

Many beginners forget this.

Wrong:

Example Code

javascript
Try it Yourself
const response = await fetch("missing.txt");

const text = await response.text();

Better:

Example Code

javascript
Try it Yourself
if (!response.ok) {

  console.log(response.status);

}

Handle Promise Rejections

Example

Example Code

javascript
Try it Yourself
fetch("missing.txt")

.then(function(response){

  return response.text();

})

.catch(function(err){

  console.log(err);

});

Explain the difference between:

  • Rejected Promise
  • HTTP error

Use finally()

Example

Example Code

javascript
Try it Yourself
showSpinner();

try {

  ...

}

finally {

  hideSpinner();

}
Cleanup code belongs in finally().

Debugging HowTo

  • Add try...catch around awaited code.
  • Check both response.ok and response.status.
  • Use breakpoints on await lines.
  • Use the Network tab.

Debugging Checklist

  • Did the Promise resolve?
  • Did you forget await?
  • Did you check response.ok?
  • Is your code inside an async function?
  • Did the request appear in the Network tab?
  • Are errors reported in the Console?
  • Are you blocking the Event Loop?

Summary

  • Use console.log() to trace execution.
  • Inspect Promise objects while debugging.
  • Use try...catch to handle rejected Promises.
  • Use the browser's Network tab to inspect requests.
  • Set breakpoints to inspect variables and execution.
  • Check response.ok after every fetch().

Interactive Code Examples

Use console.log()

javascript
Try it Yourself
Interactive runnable JavaScript example from EA Academy.
Live Output Preview:

Use a display function

javascript
Try it Yourself
JavaScript syntax and algorithm demonstration.
myDisplayer("Before fetch");



const promise = fetch("fetch.txt");



myDisplayer("After fetch");

JS Example 3

javascript
Try it Yourself
JavaScript syntax and algorithm demonstration.
// Async function to download a file

async function loadText(file) {

  const response = await fetch(file);

  myDisplayer(response);

}



myDisplayer("Start");



// Try to load a missing file

loadText("missing.txt");



myDisplayer("Done");

JS Example 4

javascript
Try it Yourself
JavaScript syntax and algorithm demonstration.
// Async function to download a file

async function loadData(file) {

  try {

    let response = await fetch(file);

    let data = await response.json()

    myDisplayer(JSON.stringify(data));

  } catch (error) {

    myDisplayer(error);

  }

}



// Call async function

loadData("customer.json")

JS Example 5

javascript
Try it Yourself
Interactive runnable JavaScript example from EA Academy.
Live Output Preview:

JS Example 6

javascript
Try it Yourself
JavaScript syntax and algorithm demonstration.
// Async function to download a file

async function loadData(file) {

  let response = await fetch(file);

  myDisplayer(response.status);

  myDisplayer(response.headers.get("content-type"));

}