← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

JavaScript Event Loop

javascript ES5 Intermediate
debt(d7/e7/b5/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints list chrome-devtools and lighthouse as tools — these can surface jank or long tasks in profiling/performance panels, but only after the fact at runtime. There is no static analysis that reliably catches event-loop-blocking patterns before deployment; the symptom (UI freeze, delayed callbacks) only manifests under real load or careful profiling.

e7 Effort Remediation debt — work required to fix once spotted

Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix says to move heavy computation to a Web Worker or chunk it with requestIdleCallback. Introducing Web Workers requires a new threading boundary, message-passing architecture, and rethinking data flow — this is not a single-file change. Long blocking synchronous operations may be spread across many call sites, making remediation a cross-cutting concern rather than a localised patch.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (d5, scored b5). applies_to is 'web' context only, limiting reach somewhat compared to universal concerns. However, the event loop is a fundamental JavaScript execution model — every async feature (Promises, setTimeout, fetch, UI events) is shaped by it. Teams must continuously reason about microtask vs macrotask ordering, blocking vs non-blocking code, and main-thread budget, imposing an ongoing productivity tax across many work streams.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field explicitly states that setTimeout(fn, 0) is widely believed to execute 'immediately after the current line' but actually runs after the call stack clears AND all pending microtasks (Promises) resolve. This ordering (microtasks before macrotasks) contradicts how developers reason about 'zero delay' and is a well-documented but frequently missed gotcha that causes subtle ordering bugs.

About DEBT scoring →

Also Known As

JavaScript event loop call stack event loop microtask macrotask

TL;DR

The mechanism that lets JavaScript execute non-blocking I/O despite being single-threaded — call stack, microtask queue, and macrotask queue.

Explanation

JavaScript runs on a single thread. The event loop coordinates: the Call Stack (synchronous execution), the Microtask Queue (Promises, queueMicrotask — drained completely after every task), and the Macrotask Queue (setTimeout, setInterval, I/O callbacks — one per loop iteration). When the stack is empty, microtasks run first (all of them), then one macrotask, then microtasks again, and so on. Understanding this explains why Promise.resolve().then() fires before setTimeout(fn, 0), and why a long synchronous task blocks the UI — it starves the event loop. In PHP terms this maps conceptually to Swoole's coroutine scheduler or ReactPHP's event loop, though PHP isolates workers per request by default.

Diagram

flowchart TD
    CALL[Call Stack] -->|empty?| EL{Event Loop}
    EL -->|check first| MICRO[Microtask Queue<br/>Promise.then / await]
    MICRO -->|drain completely| CALL
    EL -->|then check| MACRO[Macrotask Queue<br/>setTimeout / setInterval / I/O]
    MACRO -->|one at a time| CALL
    WEB[Web APIs<br/>fetch, timers, DOM] -->|callback ready| MACRO
    INFO[Microtasks always drain<br/>before next macrotask]
style MICRO fill:#238636,color:#fff
style MACRO fill:#1f6feb,color:#fff
style CALL fill:#6e40c9,color:#fff

Common Misconception

setTimeout(fn, 0) executes the callback immediately after the current line. setTimeout with 0ms delay schedules the callback as a macrotask — it runs after the current call stack clears and all pending microtasks (Promises) resolve. It is never truly immediate.

Why It Matters

JavaScript is single-threaded with an event loop — understanding the call stack, microtask queue (Promises), and macrotask queue (setTimeout) is essential to predict execution order and avoid UI freezes.

Common Mistakes

  • Long synchronous operations that block the event loop — the UI freezes and no events are processed.
  • Not understanding that Promise callbacks (microtasks) run before setTimeout callbacks (macrotasks).
  • Assuming setTimeout(fn, 0) runs immediately — it runs after all microtasks and the current synchronous code.
  • Heavy computation in the main thread instead of a Web Worker.

Code Examples

✗ Vulnerable
// Blocking the event loop — UI freezes:
function processLargeArray(arr) {
    for (let i = 0; i < arr.length; i++) {
        heavyComputation(arr[i]); // Blocks for 5s — page unresponsive
    }
}

// Non-blocking — yield to event loop:
async function processChunked(arr) {
    for (let i = 0; i < arr.length; i += 1000) {
        processChunk(arr.slice(i, i + 1000));
        await new Promise(r => setTimeout(r, 0)); // Yield
    }
}
✓ Fixed
// Microtasks (Promises) always run before the next macrotask (setTimeout)
console.log('1 — sync');

setTimeout(() => console.log('4 — macrotask'), 0);

Promise.resolve().then(() => console.log('2 — microtask'));

console.log('3 — sync');
// Output: 1, 3, 2, 4

// Blocking the event loop — don't do this:
function blockFor(ms) {
  const end = Date.now() + ms;
  while (Date.now() < end) {} // busy wait — UI freezes
}

// Non-blocking alternative:
await new Promise(resolve => setTimeout(resolve, ms));

// Break up long CPU work:
async function processLargeArray(items) {
  for (let i = 0; i < items.length; i++) {
    process(items[i]);
    if (i % 1000 === 0) await scheduler.yield(); // breathe
  }
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 36
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 0 pings F 2 pings S 4 pings S 0 pings M 1 ping T 0 pings W 1 ping T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S
No pings yet today
No pings yesterday
Amazonbot 7 Perplexity 7 Google 4 ChatGPT 4 Unknown AI 3 SEMrush 3 Ahrefs 2 Majestic 1
crawler 27 crawler_json 3 pre-tracking 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Never block the main thread with CPU-intensive synchronous work — move heavy computation to a Web Worker or break it into chunks with requestIdleCallback
📦 Applies To
javascript ES5 web
🔗 Prerequisites
🔍 Detection Hints
Large synchronous loop or JSON.parse of large data on main thread causing jank; setTimeout(fn,0) misunderstanding
Auto-detectable: ✓ Yes chrome-devtools lighthouse
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: High Context: File Tests: Update

✓ schema.org compliant