<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm" -->

---
title: JavaScript Event Loop Explained: Queue Tracing and...
description: A deep technical walkthrough teaches developers to manually trace JavaScript async execution order using step-by-step queue trace tables covering the call...
canonical: https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: JavaScript Event Loop Explained: Queue Tracing and Debugging Guide | daily.dev
og:description: A deep technical walkthrough teaches developers to manually trace JavaScript async execution order using step-by-step queue trace tables covering the call...
og:url: https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm
og:image: https://api.daily.dev/og/posts/sA0ThbCRM.png
og:image:alt: JavaScript Event Loop Explained: Queue Tracing and Debugging Guide
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript Event Loop Explained: Queue Tracing and Debugging Guide

**[SitePoint](https://daily.dev/sources/sitepoint)** · 16 min read · 88 upvotes · 4 comments

## Summary

A deep technical walkthrough teaches developers to manually trace JavaScript async execution order using step-by-step queue trace tables covering the call stack, microtask queue, and task queue. It explains the HTML Living Standard's event loop model, including multiple task queues and microtask checkpoints, works through annotated code examples (Promise vs setTimeout, microtask starvation with recursive chains), and contrasts browser behavior with Node.js's six-phase libuv event loop, including process.nextTick and setImmediate ordering quirks. It closes with practical debugging techniques using Chrome DevTools' Performance panel, Node's --inspect flag, async_hooks, and diagnostic reports, plus common async bugs like reading promise results synchronously.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.sitepoint.com/javascript-event-loop-explained-debugging-guide>

## Questions this post answers

### Why does a Promise.resolve().then() callback run before a setTimeout(fn, 0) callback in JavaScript?

Microtasks, including promise then/catch/finally callbacks, always drain completely before the event loop moves on to the next task, and setTimeout callbacks are tasks (macrotasks). After synchronous code finishes and the call stack empties, a microtask checkpoint runs that fully drains the microtask queue, even processing microtasks newly enqueued during that same checkpoint, before any setTimeout callback can execute.

_daily.dev surfaces explainers like this for developers debugging async ordering bugs in production code._

### What causes microtask starvation in JavaScript and how do I detect it?

Microtask starvation happens when a chain of promise callbacks keeps enqueuing more microtasks, so the microtask checkpoint never finishes draining and the event loop never reaches the next task or rendering step. In Chrome DevTools' Performance panel, it appears as one extremely long 'Task' bar (over the 50ms long-task threshold) with no rendering frames interleaved. Fixing it means periodically yielding via setTimeout, MessageChannel, or await scheduler.yield() (supported in Chromium and Firefox 142+, not Safari).

_Developers hunting frozen UI bugs can track event-loop debugging techniques like this via daily.dev._

### How does process.nextTick ordering differ from promise microtasks in Node.js, and does it change between CommonJS and ES modules?

In CommonJS scripts, process.nextTick callbacks run before promise microtasks because nextTick occupies a separate, higher-priority queue that drains first. In ES modules, this flips for top-level code: promise callbacks print before nextTick, because the module itself is evaluated inside a promise job. This behavior is consistent across all current Node.js LTS releases (v18+), a change introduced in v11.0.0 that aligned Node's microtask draining more closely with browser semantics.

_Node.js developers debugging async ordering across CommonJS and ESM can keep these distinctions handy via daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@pdfopsdev** · 6 upvotes

> the node phase stuff trips people up way more than microtasks tbh. setImmediate vs setTimeout(fn,0) ordering flips depending on whether you're inside an I/O callback or not, and the libuv phase diagrams never make that obvious until you've chased the bug once.

**@ristotoldsep** · 3 upvotes

> If you can trace microtasks by hand, async bugs lose half their magic.

**@nick\_liang** · 0 upvotes

> Good read. I had no idea the nextTick vs promise order flips in an ES module, that would've driven me nuts. The microtask starvation part is also a good reminder that a recursive .then chain can freeze the UI.

## Similar posts on daily.dev

- [Mastering the JavaScript Event Loop](https://daily.dev/posts/mastering-the-javascript-event-loop-xrcdqa1q5) · freeCodeCamp · 26 upvotes · 0 comments
- [How the JavaScript Runtime Schedules Callbacks: A Look at the Event Loop](https://daily.dev/posts/how-the-javascript-runtime-schedules-callbacks-a-look-at-the-event-loop-qkr1ufg7o) · Medium · 4 upvotes · 0 comments
- [Stop Calling Node.js “Single-Threaded”: A Look Under the Hood](https://daily.dev/posts/stop-calling-node-js-single-threaded-a-look-under-the-hood-ofsmhbjmm) · Medium · 6 upvotes · 0 comments
- [Medium](https://daily.dev/posts/medium-w2o7cicnw) · Medium · 0 upvotes · 0 comments

---

Tags: [#javascript](https://daily.dev/tags/javascript), [#nodejs](https://daily.dev/tags/nodejs)

[View this post on daily.dev](https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"JavaScript Event Loop Explained: Queue Tracing and Debugging Guide","url":"https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm"},"datePublished":"2026-10-09T16:38:06.765Z","dateModified":"2026-10-09T16:51:16.603Z","description":"A deep technical walkthrough teaches developers to manually trace JavaScript async execution order using step-by-step queue trace tables covering the call...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/f1b924a2feb217c0c92e9f117b10cfab?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/f1b924a2feb217c0c92e9f117b10cfab?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"SitePoint","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"SitePoint","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/sitepoint","url":"https://daily.dev/sources/sitepoint"},"commentCount":4,"discussionUrl":"https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":88},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":4}],"keywords":"javascript,nodejs","timeRequired":"PT16M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"SitePoint","item":"https://daily.dev/sources/sitepoint"},{"@type":"ListItem","position":3,"name":"JavaScript Event Loop Explained: Queue Tracing and Debugging Guide"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm","comment":[{"@type":"Comment","text":"the node phase stuff trips people up way more than microtasks tbh. setImmediate vs setTimeout(fn,0) ordering flips depending on whether you’re inside an I/O callback or not, and the libuv phase diagrams never make that obvious until you’ve chased the bug once.","datePublished":"2026-10-09T18:01:25.400Z","url":"https://daily.dev/posts/sA0ThbCRM#c-dRBNrNRza","author":{"@type":"Person","name":"PDFops","url":"https://daily.dev/pdfopsdev","image":"https://media.daily.dev/image/upload/s---8isRBKc--/f_auto/v1782922291/avatars/avatar_orjMeK8QKaaVZwGq7ScPz?_a=BAMAMicg0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":6}},{"@type":"Comment","text":"If you can trace microtasks by hand, async bugs lose half their magic.","datePublished":"2026-10-13T11:51:13.396Z","url":"https://daily.dev/posts/sA0ThbCRM#c-jyBCyu7F3","author":{"@type":"Person","name":"Risto Tõldsep","url":"https://daily.dev/ristotoldsep","image":"https://lh3.googleusercontent.com/a/ACg8ocLDWc6mZn0JwNmXXw6WY0L_HJ6pegzRttooC5VgtXESHSj3MNYx=s96-c"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":3}},{"@type":"Comment","text":"Good read. I had no idea the nextTick vs promise order flips in an ES module, that would’ve driven me nuts. The microtask starvation part is also a good reminder that a recursive .then chain can freeze the UI.","datePublished":"2026-10-14T19:12:15.603Z","url":"https://daily.dev/posts/sA0ThbCRM#c-kFisVrvz5","author":{"@type":"Person","name":"Nick Liang","url":"https://daily.dev/nick_liang","image":"https://media.daily.dev/image/upload/s--O0TOmw4y--/f_auto/v1715772965/public/noProfile"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm#faq","mainEntity":[{"@type":"Question","name":"Why does a Promise.resolve().then() callback run before a setTimeout(fn, 0) callback in JavaScript?","acceptedAnswer":{"@type":"Answer","text":"Microtasks, including promise then/catch/finally callbacks, always drain completely before the event loop moves on to the next task, and setTimeout callbacks are tasks (macrotasks). After synchronous code finishes and the call stack empties, a microtask checkpoint runs that fully drains the microtask queue, even processing microtasks newly enqueued during that same checkpoint, before any setTimeout callback can execute. daily.dev surfaces explainers like this for developers debugging async ordering bugs in production code."}},{"@type":"Question","name":"What causes microtask starvation in JavaScript and how do I detect it?","acceptedAnswer":{"@type":"Answer","text":"Microtask starvation happens when a chain of promise callbacks keeps enqueuing more microtasks, so the microtask checkpoint never finishes draining and the event loop never reaches the next task or rendering step. In Chrome DevTools' Performance panel, it appears as one extremely long 'Task' bar (over the 50ms long-task threshold) with no rendering frames interleaved. Fixing it means periodically yielding via setTimeout, MessageChannel, or await scheduler.yield() (supported in Chromium and Firefox 142+, not Safari). Developers hunting frozen UI bugs can track event-loop debugging techniques like this via daily.dev."}},{"@type":"Question","name":"How does process.nextTick ordering differ from promise microtasks in Node.js, and does it change between CommonJS and ES modules?","acceptedAnswer":{"@type":"Answer","text":"In CommonJS scripts, process.nextTick callbacks run before promise microtasks because nextTick occupies a separate, higher-priority queue that drains first. In ES modules, this flips for top-level code: promise callbacks print before nextTick, because the module itself is evaluated inside a promise job. This behavior is consistent across all current Node.js LTS releases (v18+), a change introduced in v11.0.0 that aligned Node's microtask draining more closely with browser semantics. Node.js developers debugging async ordering across CommonJS and ESM can keep these distinctions handy via daily.dev."}}]}
```

