The Event Loop

Understand how single-threaded JavaScript handles asynchronous tasks using the Call Stack, Web APIs, and the Microtask Queue.

JavaScript9 min readConcept 30 of 62

The Single-Threaded Illusion

JavaScript is strictly single-threaded, meaning it can only execute one piece of code at a time on its main thread.

To prevent the browser from freezing during slow tasks (like fetching data), JavaScript offloads work to the browser's background APIs, orchestrating the results via the Event Loop.

The Call Stack (LIFO)

The **Call Stack** is where your synchronous code actually runs. It operates on a 'Last In, First Out' (LIFO) principle.

If function A calls function B, A is paused and B is placed on top of the stack. When B finishes, it pops off, and A resumes.

If the Call Stack is busy, *nothing else can happen*. The browser cannot even render pixels until the stack is empty.

The Queues: Micro vs Macro

When a background task finishes, its callback isn't executed immediately. It is pushed into a waiting line. There are two distinct lines:

1. **The Microtask Queue**: Reserved for high-priority tasks, primarily resolved Promises (.then).

2. **The Macrotask Queue**: Reserved for standard callbacks like setTimeout, setInterval, and DOM events.

The Event Loop's only job is to watch the Call Stack. If the Stack is empty, it drains the *entire* Microtask queue first. Only when the Microtask queue is empty will it allow a Macrotask to run.

The Loop Architecture

Examine the architecture of the Event Loop to visualize how synchronous code, Web APIs, and priority queues interact.

The Loop Architecture

// 1. Synchronous (Call Stack)
console.log("Start");
// 2. Macrotask (setTimeout)
setTimeout(() => {
console.log("Timeout");
}, 0);
// 3. Microtask (Promise)
Promise.resolve().then(() => {
console.log("Promise");
});
Output Order:"Start" "Promise" "Timeout"
Call Stack (LIFO)
Executes sync code. Must be completely empty before queues can run.
The Event Loop
Watches the Call Stack.
If empty, pulls from queues.
Browser Web APIs (Background)
setTimeout
fetch()
DOM Events
Microtask Queue
Priority #1
1. Promises (.then)
Event Loop completely drains this queue first.
Macrotask Queue
1. setTimeout callbacks
2. Click events
Runs one task, then checks Microtasks again.
iWhen resolving promises, their callbacks jump into the Microtask Queue. This means a Promise will ALWAYS resolve before a zero-millisecond setTimeout, because Microtasks cut the line!

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1What happens if a function on the Call Stack takes 10 seconds to execute?
  2. 2Which queue has priority when the Call Stack becomes empty?
  3. 3If you call `setTimeout(callback, 1000)`, where does the countdown happen?

Remember this

  • JavaScript is single-threaded and executes synchronously on the Call Stack.
  • Background work (timers, network requests) is offloaded to the browser's Web APIs.
  • Resolved Promises go to the high-priority Microtask Queue.
  • setTimeout and DOM events go to the lower-priority Macrotask Queue.
  • The Event Loop only pulls from the queues when the Call Stack is completely empty.
  • The Event Loop always drains the Microtask Queue entirely before running a Macrotask.

Done with this concept?

Mark it complete to track your progress. No login needed.