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.
Log Events
The easiest way to debug asynchronous code is to log when things happen.
Use console.log()
Example Code
javascriptconsole.log("Before fetch");
const promise = fetch("fetch.txt");
console.log("After fetch");Use a display function
Example Code
javascriptmyDisplayer("Before fetch");
const promise = fetch("fetch.txt");
myDisplayer("After fetch");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// 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");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// 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")Rule 2: Check response.ok
Fetch does not reject on HTTP errors like 404.
You must check response.ok.
Example
Example Code
javascript// 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);
}
}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// Async function to download a file
async function loadData(file) {
let response = await fetch(file);
myDisplayer(response.status);
myDisplayer(response.headers.get("content-type"));
}Common Response Headers
| Header | Description |
|---|---|
| Content-Type | Type of the response (text/html, application/json, etc.) |
| Content-Length | Size of the response in bytes |
| Cache-Control | How the response may be cached |
| Expires | When the cached response expires |
| Last-Modified | When the resource was last modified |
| ETag | Resource version identifier used for caching |
| Date | Time the server generated the response |
| Server | Information about the web server |
| Access-Control-Allow-Origin | CORS policy |
| Content-Encoding | Compression 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.
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
javascriptconst response = await fetch("customer.json");Some Possible Results
| Name | Status | Type | Size | Time |
|---|---|---|---|---|
| customer.json | 200 | fetch | 548 Bytes | 134ms |
| customer.json | 404 | fetch | ||
| customer.json | 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
javascriptExample
const response = await fetch("demo.txt");
debugger;
const text = await response.text();Breakpoints on await
You can set breakpoints on await lines.
This lets you inspect values before and after async steps.
Example
Example Code
javascriptasync 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
javascriptasync 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
javascriptasync 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
javascriptfetch("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
javascriptmyDisplayer("Start");
setTimeout(function() {myDisplayer("Timer")},1000);
myDisplayer("End");Debug Parallel Operations
Result
Example Code
javascriptconst 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
javascriptconst response = await fetch("missing.txt");
const text = await response.text();Better:
Example Code
javascriptif (!response.ok) {
console.log(response.status);
}Handle Promise Rejections
Example
Example Code
javascriptfetch("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
javascriptshowSpinner();
try {
...
}
finally {
hideSpinner();
}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()
javascriptUse a display function
javascriptmyDisplayer("Before fetch");
const promise = fetch("fetch.txt");
myDisplayer("After fetch");JS Example 3
javascript// 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// 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
javascriptJS Example 6
javascript// Async function to download a file
async function loadData(file) {
let response = await fetch(file);
myDisplayer(response.status);
myDisplayer(response.headers.get("content-type"));
}