Skip to main content

Command Palette

Search for a command to run...

The Node.js Event Loop Explained

How a Single Thread Manages Thousands of Tasks Without Breaking

Updated
•4 min read•View as Markdown

The Problem: Single Thread, Many Requests

Node.js runs on a single thread. That means it can execute only one piece of code at a time.

At first glance this sounds like a limitation.

Imagine a server receiving,

  • A file read request

  • A database query

  • An API call

  • Another user request

If Node.js handled these one by one, everything would slow down quickly.

So the question is:

How does Node.js handle thousands of requests without getting stuck?

The answer is the Event Loop


What is the Event Loop?

The event loop is the task manager of Node.js.

It constantly watches,

  • What needs to be executed now

  • What is waiting to be executed

  • What has finished in the background

And then it decides what runs next.

You can think of it like a manager in a restaurant,

  • Orders come in (tasks)

  • Some are quick (serve water)

  • Some take time (cook food)

  • The manager keeps things moving without blocking everything


Why Node.js Needs an Event Loop

Because Node.js is single-threaded, it cannot

  • Wait for slow operations

  • Block execution

If it did, the entire server would freeze.

So instead:

  • It delegates slow work (like file reading, DB calls) to the system

  • Continues handling other requests

  • Comes back when the work is done

The event loop makes this coordination possible.


Call Stack vs Task Queue

To understand the event loop, you need two simple ideas,

1. Call Stack

  • Where code executes

  • Works li LIFO (Last in, First Out) order

  • Only one function runs at a time

function greet() {
  console.log("Hello");
}

greet();

Flow,

  • greet() goes into the call stack

  • Executes

  • Leaves the stack


2. Task Queue

  • Holds tasks that are ready to run later

  • Works in FIFO (First In, First Out) order


How They Work Together

The event loop does this continuously,

  1. Check if the call stack is empty

  2. If empty --> pick a task from the queue

  3. Push it to the call stack

  4. Execute it

This cycle runs forever


How Async Operations Are Handled

Let’s look at a simple example,

import fs from "fs";

console.log("Start");

fs.readFile("./file.txt", "utf-8", (err, data) => {
  console.log("File read complete");
});

console.log("End");

What actually happens:

  1. "Start" logs

  2. fs.readFile is sent to the system (background)

  3. "End" logs immediately

  4. Once file reading is done --> callback goes to task queue

  5. Event loop pushes it to call stack

  6. "File read complete" logs

Output

Start
End
File read complete

Node.js does not wait for file reading


Timers vs I/O Callbacks

Not all async tasks are treated exactly the same.

1. Timers (setTimeout, setInterval)

setTimeout(() => {
  console.log("Timer done");
}, 0);
  • Scheduled after a delay

  • Goes to a timer queue

  • Runs only when call stack is empty


2. I/O Callbacks (file, DB, network)

import fs from "fs";

fs.readFile("./file.txt", () => {
  console.log("I/O done");
});
  • Triggered when background work finishes

  • Added to a queue

  • Executed when event loop picks it


Important Insight

Even with 0ms delay,

setTimeout(() => console.log("Timer"), 0);
console.log("Sync");

Output

Sync
Timer

Because

  • Synchronous code always runs first

  • Event loop only runs queued tasks after stack is empty


Event Loop as a Queue Manager

Think of:

  • Call Stack --> Chef cooking one dish at a time

  • Task Queue --> Orders waiting

  • Event Loop --> Waiter managing flow

Flow:

  1. Chef finishes current dish

  2. Waiter brings next order from queue

  3. Repeat

Meanwhile:

  • Long tasks (like baking) happen separately

  • Once done → added back to queue


Role of Event Loop in Scalability

This is where Node.js becomes powerful.

Because of the event loop,

  • Node.js does not block

  • Can handle many concurrent requests

  • Uses less memory

  • Works great for,

    • APIs

    • Real-time apps

    • Streaming services

Instead of:
Creating new threads for every request

Node.js does:
Reuses a single thread + event loop


Small Edge Case to Understand

If you write blocking code,

while (true) {}
  • The event loop gets blocked

  • No other task runs

  • Server becomes unresponsive

This shows:

The event loop is powerful, but only if you don’t block it


4 views