Skip to main content

What is the event loop?

The event loop is a mechanism inside the JavaScript engine that manages code execution, handles asynchronous operations, and coordinates the interaction between the Call Stack and the task queues.

It is the event loop that makes JavaScript asynchronous and non-blocking, even though the language itself is single-threaded.

In short

The event loop is an "organizer" that decides which task runs now and which one waits, so the main thread does not get blocked.

The main participants

To understand the event loop, you need to know three structures:

  1. Call Stack - the call stack (where the current code executes, e.g. functions and expressions).
  2. Task Queue / Callback Queue - the queue of macrotasks (timers, events, setTimeout, setInterval, I/O).
  3. Microtask Queue - the queue of microtasks (Promise.then(), queueMicrotask(), process.nextTick() in Node.js).
  4. Event Loop - the "loop" that watches:
  • if the stack is empty → takes a task from the microtask queue;
  • if that is empty → takes a task from the macrotask queue.

How the event loop works (step by step)

  1. JS executes synchronous code (everything, top to bottom).
  2. When an asynchronous operation is encountered, it is sent off to the external Web APIs (e.g. a timer, fetch).
  3. When the operation finishes, its callback goes into the task queue.
  4. When the Call Stack is empty, the event loop takes a task from the queue and puts it on the stack for execution.
  5. After all microtasks are executed, the event loop moves on to the next macrotask.

This happens continuously, in an "infinite loop".

Visually

javascript
┌────────────────────┐ │ Call Stack │ ← runs the current code └─────────▲──────────┘ │ │ (empty?) ▼ ┌────────────────────┐ │ Microtask Queue │ ← Promise, queueMicrotask └─────────▲──────────┘ │ │ (empty?) ▼ ┌────────────────────┐ │ Task Queue │ ← setTimeout, setInterval, I/O └─────────▲──────────┘ │ │ ▼ The event loop checks the queues and moves tasks onto the stack

Example with setTimeout and Promise

javascript
console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4');

Output:

javascript
1 4 3 2

Why?

  1. Synchronous code runs first (1, 4);
  2. The promise (.then) goes into the microtasks;
  3. setTimeout goes into the macrotasks;
  4. The event loop runs all the microtasks first (3), then takes the next macrotask (2).

An example of the event loop "hanging"

javascript
setTimeout(() => console.log('2'), 0); while (true) {} // infinite loop

The event loop will never reach setTimeout, because the stack never frees up. Asynchronous tasks run only when the stack is empty.

The difference between macrotasks and microtasks

Task typeExamplesWhen it runs
MicrotasksPromise.then, queueMicrotaskRight after the current code and before the next macrotask
MacrotaskssetTimeout, setInterval, fetch, I/O, DOM eventsAfter all microtasks

In other words, microtasks have higher priority.

In the browser vs in Node.js

EnvironmentEvent loop specifics
BrowserWeb APIs provide timers, fetch, DOM events
Node.jsHas its own queues (phases): timers, I/O, check, close, nextTick, and so on

But in both cases the idea is the same: one thread, asynchrony through the event loop.

SUMMARY

ComponentPurpose
Call StackRuns the current code
Web APIsHandle asynchronous operations
Task QueueHolds macrotask callbacks
Microtask QueueHolds promises and the microqueue
Event LoopMoves ready tasks from the queues onto the stack

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.