Events in Node.js

Node.js provides the EventEmitter class for event-driven programming outside the browser. Many Node.js core modules (streams, HTTP servers, file system watchers) are built on EventEmitter.

The EventEmitter Class

Import EventEmitter from the events module:

const EventEmitter = require("events");
// or in ES modules:
// import { EventEmitter } from 'events';

const emitter = new EventEmitter();

Basic Usage

Register listeners with on and trigger events with emit:

const EventEmitter = require("events");
const emitter = new EventEmitter();

// Register a listener
emitter.on("greet", (name) => {
  console.log(`Hello, ${name}!`);
});

// Emit the event
emitter.emit("greet", "Alice");  // Hello, Alice!
emitter.emit("greet", "Bob");  // Hello, Bob!

Passing Multiple Arguments

emit can pass multiple arguments to listeners:

emitter.on("userCreated", (id, name, email) => {
  console.log(`New user: ${name} (${email}) with ID ${id}`);
});

emitter.emit("userCreated", 1, "Alice", "alice@example.com");

Once: Single-Use Listeners

Use once for listeners that should run only once:

emitter.once("ready", () => {
  console.log("Application is ready!");
});

emitter.emit("ready");  // Logs message
emitter.emit("ready");  // Nothing happens

Removing Listeners

Remove specific listeners with off or removeListener:

function handleData(data) {
  console.log("Received:", data);
}

emitter.on("data", handleData);
emitter.off("data", handleData);

// Or remove all listeners for an event
emitter.removeAllListeners("data");

Creating Custom Event Emitters

Extend EventEmitter to create your own event-driven classes. This is a common pattern in Node.js:

const EventEmitter = require("events");

class TaskQueue extends EventEmitter {
  constructor() {
    super();
    this.tasks = [];
  }

  addTask(task) {
    this.tasks.push(task);
    this.emit("taskAdded", task);
  }

  processNext() {
    if (this.tasks.length === 0) {
      this.emit("empty");
      return;
    }

    const task = this.tasks.shift();
    this.emit("taskStarted", task);

    // Simulate async work
    setTimeout(() => {
      this.emit("taskCompleted", task);
    }, 1000);
  }
}

// Usage
const queue = new TaskQueue();

queue.on("taskAdded", (task) => console.log("Added:", task));
queue.on("taskStarted", (task) => console.log("Started:", task));
queue.on("taskCompleted", (task) => console.log("Completed:", task));
queue.on("empty", () => console.log("Queue is empty"));

queue.addTask("Send email");
queue.addTask("Generate report");
queue.processNext();

Error Handling with error Events

EventEmitter has special handling for error events. If an error event is emitted with no listeners, Node.js throws an exception:

const emitter = new EventEmitter();

// This will crash the process!
emitter.emit("error", new Error("Something went wrong"));

// Always add an error listener
emitter.on("error", (err) => {
  console.error("Error occurred:", err.message);
});

Practical Example: File Watcher

const EventEmitter = require("events");
const fs = require("fs");

class FileWatcher extends EventEmitter {
  constructor(filepath) {
    super();
    this.filepath = filepath;
  }

  start() {
    fs.watch(this.filepath, (eventType, filename) => {
      if (eventType === "change") {
        this.emit("change", filename);
      } else if (eventType === "rename") {
        this.emit("rename", filename);
      }
    });

    this.emit("started", this.filepath);
  }
}

const watcher = new FileWatcher("./data.json");

watcher.on("started", (path) => console.log(`Watching: ${path}`));
watcher.on("change", (file) => console.log(`File changed: ${file}`));
watcher.on("rename", (file) => console.log(`File renamed: ${file}`));

watcher.start();

Useful EventEmitter Methods

Method Description
on(event, listener) Add a listener
once(event, listener) Add a one-time listener
off(event, listener) Remove a listener
emit(event, ...args) Trigger an event
listenerCount(event) Get number of listeners
eventNames() Get array of event names with listeners

Browser vs Node.js Comparison

Feature Browser (DOM) Node.js (EventEmitter)
Add listener addEventListener on / addListener
Remove listener removeEventListener off / removeListener
Trigger event dispatchEvent emit
One-time listener { once: true } once
Pass data CustomEvent({ detail }) Arguments to emit