๐ข Debugging Asynchronous JavaScript
Asynchronous code (like setTimeout
, fetch
, or async/await
) can be tricky to debug because it doesn't execute in a predictable top-down order.
๐ 1. Understand the Call Stack and Event Loop
Async functions are scheduled via the event loop. Use the Call Stack, Event Queue, and Network tabs in DevTools to observe when callbacks run.
๐ 2. Use async
/await
for Cleaner Debugging
Instead of chaining .then()
, use async
/await
to keep your code linear and easier to step through.
async function fetchData() {
try {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
console.log(data); // Easier to log and trace
} catch (err) {
console.error('Error:', err);
}
}
fetchData();
๐ 3. Set Breakpoints Inside Async Functions
In browser DevTools, set breakpoints inside async
functions or inside .then()
blocks to pause execution at the right moment.
๐ 4. Use console.trace()
in Callbacks
This prints the stack trace to show where the callback originated from:
setTimeout(() => {
console.trace("Inside timeout");
}, 1000);
๐ต๏ธ 5. Watch Promise States
In Chrome DevTools, go to the Sources tab โ Async Call Stack section to track unresolved promises or pending async calls.
๐งช 6. Use Network Tab for Async HTTP Calls
If you're debugging fetch()
or AJAX, use the Network tab to check status codes, payloads, and response bodies.
๐งฐ Bonus Tips
- Temporarily slow network (set to โSlow 3Gโ) in DevTools to test delays
- Use
await new Promise(resolve => setTimeout(resolve, 1000))
to simulate waiting - Log before and after async calls to trace execution flow
- Use linters to catch unhandled promises or missing
await
try...catch
to avoid silent failures.