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 |