Understanding JavaScript Event Loop: Theory and Practice

This article explains the JavaScript Event Loop mechanism in depth, covering the call stack, heap memory, macro‑ and micro‑tasks, and their queues, and demonstrates the execution order through detailed examples and step‑by‑step visualizations of multiple event‑loop cycles.

UC Tech Team
UC Tech Team
UC Tech Team
Understanding JavaScript Event Loop: Theory and Practice

Welcome to the UC International Technology public account, which shares high‑quality technical articles on client‑side, server‑side, algorithms, testing, data, and front‑end topics.

Theoretical Part

Function Call Stack (Stack) – The stack is a FILO (First In Last Out) structure. Each function call pushes the function and its local variables onto the stack. For example, calling test(1) pushes test onto the stack, then return test2(n) pushes test2. When test2 returns, it is popped, followed by test, producing the output 124.

Heap Memory (Heap) – The heap stores objects (reference‑type variables). The variable name itself remains in the stack, while the actual object resides in the heap. Assigning let b = a creates a reference in the stack that points to the same heap object as a.

Asynchronous Queue (Event Queue) – Because JavaScript is single‑threaded, time‑consuming tasks are placed into an asynchronous queue. After the main thread finishes its synchronous work, the engine repeatedly polls the queue, processes tasks, and thus forms the Event Loop.

Event Loop – Each iteration of the Event Loop processes one macro‑task from the Macro event queue, then drains all micro‑tasks from the Micro event queue before the next iteration. The two types of tasks never mix.

Key concepts: macrotasks (e.g., setTimeout, I/O) and microtasks (e.g., process.nextTick, Promise callbacks). Macro tasks reside in the Macro event queue, micro‑tasks in the Micro event queue. For each loop, the engine executes one macro‑task, then all pending micro‑tasks.

Engine Differences – How an engine schedules macro‑ and micro‑tasks varies, so the same code may produce different outputs in Chrome, Firefox, or Safari.

Practical Part

First Round of Event Loop

1. Main flow outputs: 1, 4, 7. 2. Execute the first micro‑task queue: output 3. 3. Execute the second micro‑task queue: output 5. 4. Micro‑task queue is empty; first round ends.

Second Round

1. Main flow outputs 2. 2. Micro‑task queue is empty; second round ends.

Third Round

1. Main flow outputs 6.

Fourth Round

1. After outputting 8, resolve pushes the then callback into the micro‑task queue. 2. Execute the then callback, output 9. 3. A new setTimeout is added to the macro queue. 4. No more tasks; the loop ends.

Fifth Round

1. setTimeout 10 runs in the main flow, output 10. 2. Encounter a promise, output 11. 3. resolve pushes the then callback to the micro‑task queue. 4. Execute the micro‑task, output 12. 5. Loop finishes.

Finally, the complete execution sequence of the example code is: 1, 4, 7, 3, 5, 2, 6, 8, 9, 10, 11, 12 “UC International Technology” is dedicated to sharing high‑quality technical articles; follow the public account and share the content with friends.

JavaScriptconcurrencyNode.jsAsyncEvent Loop
UC Tech Team
Written by

UC Tech Team

We provide high-quality technical articles on client, server, algorithms, testing, data, front-end, and more, including both original and translated content.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.