🔥 0
0
Lesson 5 of 10 15 min +75 XP

The Event Loop

This is the most important concept in Node.js. Understanding the event loop explains why Node.js is fast and how to write efficient code.

The Problem: Blocking Code

Imagine a restaurant with one waiter:

Blocking approach:
  • Take order from Table 1
  • Go to kitchen, wait until food is ready (10 minutes)
  • Bring food to Table 1
  • Now take order from Table 2...

Tables 2, 3, 4 wait forever. Terrible service.

Non-blocking approach:
  • Take order from Table 1, give to kitchen
  • Take order from Table 2, give to kitchen
  • Take order from Table 3, give to kitchen
  • Food ready for Table 1? Deliver it.
  • Food ready for Table 3? Deliver it.

Same one waiter, but efficient. This is Node.js.

Blocking Code Example

// blocking.js
const fs = require('fs');

console.log('1. Starting...');

// This BLOCKS - nothing else runs until file is read
const data = fs.readFileSync('large-file.txt', 'utf8');
console.log('2. File read, length:', data.length);

console.log('3. Done');
Output:
1. Starting...
(waits 2 seconds while reading file)
2. File read, length: 1000000
3. Done

If 1000 users hit your server during that 2-second wait, they all wait.

Non-Blocking Code Example

// non-blocking.js
const fs = require('fs');

console.log('1. Starting...');

// This is NON-BLOCKING - Node continues immediately
fs.readFile('large-file.txt', 'utf8', (err, data) => {
  console.log('3. File read, length:', data.length);
});

console.log('2. This runs while file is being read!');
Output:
1. Starting...
2. This runs while file is being read!
(file finishes reading)
3. File read, length: 1000000

Notice: "2" prints before "3"! Node didn't wait.

How the Event Loop Works

   ┌───────────────────────────┐
┌─>│         timers            │ <- setTimeout, setInterval
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │ <- I/O callbacks
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │           poll            │ <- Retrieve new I/O events
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │           check           │ <- setImmediate
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘
Simplified version:
  • Run your code
  • Check if any async operations finished
  • Run their callbacks
  • Repeat until nothing left to do

See It In Action

// event-loop-demo.js
console.log('1. Synchronous - runs first');

setTimeout(() => {
  console.log('4. setTimeout - runs after 0ms delay');
}, 0);

Promise.resolve().then(() => {
  console.log('3. Promise - runs before setTimeout');
});

console.log('2. Synchronous - runs second');
Output:
1. Synchronous - runs first
2. Synchronous - runs second
3. Promise - runs before setTimeout
4. setTimeout - runs after 0ms delay

Even with 0ms delay, setTimeout runs after Promises. The event loop has priorities!

Real-World Impact: Server Example

Blocking server (BAD):
// bad-server.js
const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  // BLOCKS - while reading, no other requests are handled
  const data = fs.readFileSync('big-file.txt', 'utf8');
  res.end(data);
});

server.listen(3000);

If the file takes 100ms to read, you can only handle 10 requests/second.

Non-blocking server (GOOD):
// good-server.js
const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  // NON-BLOCKING - can handle other requests while reading
  fs.readFile('big-file.txt', 'utf8', (err, data) => {
    res.end(data);
  });
});

server.listen(3000);

While one file is being read, Node handles other requests. Thousands of concurrent connections, one thread.

Common Blocking Operations to Avoid

// BAD - These block the event loop
fs.readFileSync()
fs.writeFileSync()
crypto.pbkdf2Sync()
JSON.parse(hugeString)  // Can block if string is massive
while (condition) { }   // Infinite or long loops

// GOOD - Non-blocking alternatives
fs.readFile()
fs.writeFile()
crypto.pbkdf2()
// For CPU-heavy tasks: use Worker Threads

Try It Yourself

Create this file to see blocking in action:

// blocking-test.js
const fs = require('fs');

function heavyComputation() {
  // Simulate CPU-heavy work (this BLOCKS!)
  const start = Date.now();
  while (Date.now() - start < 2000) {
    // Busy loop for 2 seconds
  }
  console.log('Heavy computation done');
}

console.log('Starting...');

setTimeout(() => {
  console.log('Timeout callback (should run after 100ms)');
}, 100);

heavyComputation(); // This blocks the event loop!

console.log('After heavy computation');
Output:
Starting...
(waits 2 seconds - the setTimeout is BLOCKED!)
Heavy computation done
After heavy computation
Timeout callback (should run after 100ms)

The timeout was set for 100ms but actually ran after 2+ seconds because the heavy computation blocked everything.

Crio.Do

Practice with Real Projects

Build and deploy Node.js applications on pre-configured cloud environments. No local setup headaches.

Start Free Trial

Key Takeaways

  • Blocking code stops everything until it finishes
  • Non-blocking code starts operations and continues immediately
  • Event loop manages callbacks when async operations complete
  • Always prefer async versions: readFile over readFileSync
  • Single thread + event loop = thousands of concurrent connections

Next, let's work with files using the fs module!

🧠 Quick Quiz

Test your understanding of this lesson.

1

What is 'blocking' code?

2

Why can Node.js handle thousands of connections with a single thread?

3

Which is better for a server: readFileSync or readFile?

npm & Packages