Welcome to the world of server-side JavaScript! Node.js isn't a programming language; it's a runtime environment that allows you to run JavaScript code outside of a web browser. It's built on Google's V8 JavaScript engine, the same one used in Chrome.

What makes Node.js special? Non-Blocking I/O

Node.js operates on a single-threaded, event-driven architecture. This sounds like a limitation, but it's the key to its performance. Instead of waiting for slow operations (like reading a file or making a database query) to complete, Node.js registers a callback and moves on to the next task. When the slow operation finishes, the callback is executed. This is called non-blocking I/O (Input/Output), and it allows a single Node.js process to handle thousands of concurrent connections efficiently.

NPM Modules: Building Blocks of Node

You can't build a large application in a single file. Node.js has a powerful module system that allows you to organize your code into separate files and share them.

  • CommonJS (require): The original module system in Node. You use require() to import a module and module.exports to expose parts of it.

JavaScript


// math.js
const add = (a, b) => a + b;
module.exports = { add };

// app.js
const math = require('./math.js');
console.log(math.add(5, 3)); // Outputs: 8
  • ES Modules (import/export): The modern JavaScript standard, now fully supported in Node.js. You need to either use the .mjs file extension or set "type": "module" in your package.json.

JavaScript


// math.mjs
export const add = (a, b) => a + b;

// app.mjs
import { add } from './math.mjs';
console.log(add(5, 3)); // Outputs: 8

The Event Loop: Node's Beating Heart

The event loop is the mechanism that enables Node's non-blocking concurrency. Here’s a simplified breakdown of how it works:

  1. Call Stack: This is where your JavaScript code is executed. When you call a function, it's pushed onto the stack. When it returns, it's popped off.
  2. Node APIs: When you call an asynchronous function (like fs.readFile), the operation is handed off to native, C++ APIs outside of the main JavaScript thread. A callback is registered to be run upon completion.
  3. Callback Queue (or Task Queue): When an asynchronous operation completes, its callback function is placed in the callback queue.
  4. The Loop: The event loop's job is simple: it constantly checks if the call stack is empty. If it is, it takes the first item from the callback queue and pushes it onto the call stack for execution.

This cycle allows Node.js to remain responsive and handle new requests while waiting for slow I/O operations to complete in the background.

JavaScript


console.log('First');

// This is an async operation. It's handed off to the Node APIs.
setTimeout(() => {
  console.log('Second (from timeout)');
}, 0);

console.log('Third');

// Output:
// First
// Third
// Second (from timeout)

Even with a 0ms delay, setTimeout is asynchronous. Its callback is placed in the queue, and the event loop only executes it after the synchronous code (console.log('Third')) has finished and the call stack is empty.