C
JavaScript/Async/Lesson 15

Event Loop — *Why JS Is Fast Despite Being Single-Threaded*

45 min·theory
This chapter
1/3

Event Loop — *Why JS Is Fast Despite Being Single-Threaded*

🎯 After reading this lesson

By the time you finish this lesson, you'll be able to confidently handle the following three things.

  • ✅ Event capturing, bubbling, and the delegation pattern
  • ✅ Memory leaks caused by missing addEventListener cleanup
  • ✅ When to apply debounce / throttle

Keep these learning goals as a checklist — once you can answer all of them, you're done with the lesson.

Single-Threaded + Event Loop

The One-Line Summary

JS is a single-threaded language that processes only one thing at a time, yet it can handle countless tasks concurrently. The secret is the Event Loop + delegating async work to the outside world.

Single-Threaded — A Trap?

javascript
console.log("1");
slowOperation();        // takes 5 seconds
console.log("2");

If slowOperation takes 5 seconds, nothing else can run during that time. User clicks are ignored too. That's blocking.

The solution: hand off blocking work to the outside, and JS keeps moving on to the next task.

How the Event Loop Works

code
┌──────────────────┐
│  Call Stack      │   ← functions currently executing
│  (single thread) │
└────────┬─────────┘
         │ when async work finishes
         │
┌────────▼─────────┐
│   Task Queue     │   ← waiting callbacks: setTimeout, events, etc.
└────────┬─────────┘
         │ picked up when Stack is empty
         ▼
       Event Loop (continuously cycling)

The browser / Node.js handles async operations (timers, network, file I/O) on separate threads. When they finish, the callback is placed in the Queue, and once the Stack is empty, the Event Loop picks it up and runs it.

The Most Famous Quiz

javascript
console.log("1");
setTimeout(() => console.log("2"), 0);
console.log("3");

// Output: 1, 3, 2

setTimeout(..., 0) — so why does 2 print last?

  • The setTimeout callback goes to the Task Queue
  • It's processed after the current code (console.log("3")) finishes

A delay of 0 in setTimeout doesn't mean immediately — it means as soon as possible after the current code finishes.

Microtask vs Macrotask

There are 2 types of queues:

  • Microtask — Promise, queueMicrotask. Higher priority
  • Macrotask — setTimeout, events, I/O. Lower priority
javascript
console.log("1");
setTimeout(() => console.log("2"), 0);          // Macrotask
Promise.resolve().then(() => console.log("3"));  // Microtask
console.log("4");

// Output: 1, 4, 3, 2

Once the current code finishes:
1. All Microtasks are processed first (3)
2. Then one Macrotask (2)

Async = Delegating to the Outside

javascript
// Synchronous — JS handles it directly
const x = 5 + 3;

// Asynchronous — delegated to the outside
setTimeout(() => {}, 1000);       // browser timer
fetch('/api');                     // browser network
fs.readFile(...);                  // Node file system

These are handled by the environment (browser / Node), not JS itself. When complete, the callback is queued. JS doesn't wait — it keeps executing the next line.

Why It's Fast

Even with 10,000 concurrent users, there's no need to spawn a separate thread per user:

  • Request 1 starts → DB query delegated externally → handle next request
  • Request 2 starts → DB query delegated → handle yet another request
  • DB response arrives → each callback is processed

One thread, countless concurrent operations. This is how Node.js and Nginx handle high traffic with minimal memory.

Pitfall — CPU-Bound Work

javascript
// ❌ Heavy computation — blocks the main thread
for (let i = 0; i < 1e9; i++) {
    sum += i;
}
// Clicks and rendering are all frozen during this

JS's async advantage applies only to I/O operations. CPU-bound work is still blocking.

Solutions:

  • Break it into small chunks and schedule with setTimeout
  • Web Worker (browser) / Worker Threads (Node) — separate thread

Summary

  • JS = single-threaded + Event Loop
  • Async work is delegated externally
  • Microtask (Promise) > Macrotask (setTimeout)
  • Strong at I/O, weak at CPU
  • CPU-bound work → use a Worker

⚡ Try It Yourself — Microtask vs Macrotask Order

Predict the output order of the code below, then run it. Promise (microtask) > setTimeout (macrotask).
✏️ JS 코드
📟 Console output
▶ Press the Run button
⚠️ Runs in a browser sandbox — only console.log() is supported; alert/fetch are not.

🤖 Try asking AI like this

Knowing the concepts in this lesson lets you give AI specific instructions. Instead of a vague "fix this," you can make vocabulary-driven requests — that's the starting point for saving tokens.

  • "Apply a 300ms debounce to this click event"
  • "Add cleanup to this event handler (return () => removeEventListener)"

Why This Saves Tokens

When you don't know the concepts, you have to ask "What does that mean?" after every AI response. Those follow-up questions eat your tokens. Learn the concept once, and the conversation ends in a single exchange.

Read this first: Event Handling
Up next: Promise
Event Loop - JavaScript