QUICK START:HTMLCSSJavaScriptTypeScriptPythonSQLReactNext.jsNode.jsLinux & UbuntuKotlinSwiftC# / .NETJavaGoRustC++DSASystem DesignDevOpsCybersecurityAI / ML
Advanced 22 min readModule: Module 14: Event Loop Internals: Microtasks, Macrotasks & libuv Queues

Event Loop Architecture: Microtasks, Macrotasks & Queues

Explore the precise execution order of the JavaScript Event Loop: Call Stack, Microtask Queue (Promises, queueMicrotask), Macrotask Queue (Timers, I/O), and Rendering Frames.

What You Will Learn in This Lesson

  • The exact execution order of Synchronous code, Microtasks, Render Steps, and Macrotasks
  • Why the Microtask queue completely drains before any next Macrotask or Render frame can execute
  • Scheduling custom high-priority microtasks with `queueMicrotask()`
  • Comparing browser event loop phases with Node.js libuv phases (Timers, Poll, Check/setImmediate)

Introduction & Core Concept

JavaScript executes on a single-threaded event loop. Understanding the deterministic hierarchy of how synchronous frames, microtasks (Promise continuations), render steps (requestAnimationFrame), and macrotasks (setTimeout, I/O) are scheduled is essential for mastering asynchronous JavaScript.
WHY DOES THIS MATTER IN THE REAL WORLD?

Microtask starvation occurs when recursive Promise resolution freezes the main thread, preventing UI rendering and user click events from firing. Knowing how queues are prioritized prevents performance bottlenecks.

Syntax & Structure

javascript
queueMicrotask(() => { ... });
requestAnimationFrame((time) => { ... });
setTimeout(() => { ... }, 0);

Predicting Execution Order Across Event Loop Queues

javascript
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Event Loop Priority Execution Sequence
console.log("1. [Synchronous] Main script start");
// 1. Macrotask (Timer Queue)
setTimeout(() => {
console.log("6. [Macrotask] setTimeout callback executed");
}, 0);
// 2. Microtask via Promise
Promise.resolve().then(() => {
console.log("3. [Microtask] Promise.then callback");
});
// 3. Microtask via queueMicrotask API
queueMicrotask(() => {
console.log("4. [Microtask] Explicit queueMicrotask callback");
});
// 4. Nested Microtask scheduled from within a Microtask
Promise.resolve().then(() => {
console.log("5. [Microtask] Chained microtask executed BEFORE any macrotask!");
});
console.log("2. [Synchronous] Main script end");
// Output Order:
// 1. [Synchronous] Main script start
// 2. [Synchronous] Main script end
// 3. [Microtask] Promise.then callback
// 4. [Microtask] Explicit queueMicrotask callback
// 5. [Microtask] Chained microtask executed BEFORE any macrotask!
// 6. [Macrotask] setTimeout callback executed

Line-by-Line Technical Breakdown

1Rendering Pipeline Intersection: In web browsers, the rendering pipeline (Style Recalculation, Layout, Paint, Compositing) runs between macrotasks—ONLY if a frame refresh is due (e.g. 60Hz/120Hz display tick). Microtasks run immediately after JavaScript execution, before layout and paint.

Try It Yourself (Interactive Editor)

Modify the code in real-time and click Run to test live browser output and console logs.

Intelligent Code Runner & Live Sandbox[JAVASCRIPT]
JAVASCRIPT SOURCE EDITOR
Interactive Live Code

Common Mistakes & How to Avoid Them

#1: Creating infinite recursive microtasks (e.g., recursive Promise loops), starving the UI thread and freezing the page.

Because microtasks must drain completely before the browser can render or process user clicks, recursive microtasks completely freeze the browser.

Incorrect / Antipattern
function starve() { Promise.resolve().then(starve); }
starve();
Correct / Professional Solution
function processChunks() { setTimeout(processNextChunk, 0); }

Industry Best Practices & Professional Standards

  • Use `queueMicrotask()` when state updates must execute immediately after current synchronous code but before DOM rendering.
  • Use `requestAnimationFrame()` for visual layout measurements and canvas animations.
  • Use `setTimeout(fn, 0)` or `scheduler.yield()` to break long tasks into discrete chunks and keep UI responsive.

Lesson Summary & Core Takeaways

  • Execution priority: Synchronous Stack → Microtasks → Render Pipeline → Macrotasks.
  • Microtasks completely drain before the event loop advances to the next task.
  • Break long computational tasks into macrotask chunks to avoid UI freezing.