CodingNic

Core Node.js Modules

The events Module: EventEmitter

Core Node.js Modules 12 min read

The events Module: EventEmitter

Objectives

By the end of this lesson, you should be able to:

  • Create a class that extends EventEmitter and emits custom events
  • Register multiple listeners for the same event
  • Use .once() and .removeListener() to control how a listener fires

💡 Why this matters: Module 2 (Events) of the frontend track covered DOM events, a click, a keypress. Node has its own, unrelated event system, EventEmitter, and it’s the foundation a huge amount of Node itself is built on, streams (Lesson 8), HTTP servers (Lesson 6), and much of Express, all emit and listen for events using this exact pattern.

⚠️ A note on verification: every snippet and output in this lesson was actually run with Node.js.

Extending EventEmitter

javascript
const EventEmitter = require('events');

class OrderProcessor extends EventEmitter {
  process(orderId) {
    console.log(`processing order ${orderId}`);
    this.emit('completed', orderId);
  }
}

const processor = new OrderProcessor();

processor.on('completed', (orderId) => {
  console.log(`listener 1: order ${orderId} is done`);
});

processor.on('completed', (orderId) => {
  console.log(`listener 2: sending confirmation email for order ${orderId}`);
});

processor.process(101);
text
processing order 101
listener 1: order 101 is done
listener 2: sending confirmation email for order 101

class OrderProcessor extends EventEmitter (Module 2’s extends/super) gives every instance .on() and .emit(). .on('completed', callback) registers a listener, .emit('completed', orderId) fires it, passing orderId through as an argument. Multiple listeners can be registered for the same event, .emit() calls every one of them, in the exact order they were registered.

once() and removeListener()

javascript
const EventEmitter = require('events');
const emitter = new EventEmitter();

emitter.once('greet', (name) => {
  console.log(`(once) hello, ${name}`);
});

emitter.emit('greet', 'Sam');
emitter.emit('greet', 'Jordan');
text
(once) hello, Sam

.once() registers a listener that fires exactly one time, then automatically removes itself, the second .emit('greet', 'Jordan') finds no listener left, so nothing logs for it.

javascript
function onPing() {
  console.log('ping received');
}
emitter.on('ping', onPing);
emitter.emit('ping');
emitter.removeListener('ping', onPing);
emitter.emit('ping');
console.log('done, no second ping should have logged');
text
ping received
done, no second ping should have logged

.removeListener(event, handler) unregisters a specific listener, this requires the handler to be a named function (not an inline arrow function), since removal works by matching the function reference itself. After removal, emitting the same event again finds no listener there.

Why This Pattern Matters for Express

Node’s built-in http module (next lesson) emits a 'request' event every time a request arrives, and Express is built directly on top of that. Streams (Lesson 8) emit 'data' and 'end' events as data arrives. EventEmitter isn’t a niche feature, it’s the plumbing connecting most of Node’s asynchronous, I/O-driven core.

Try It

  1. Create a class Timer extending EventEmitter, with a start() method that emits a 'tick' event three times (using a loop), and register a listener that logs each tick.
  2. Register two separate listeners for the same custom event, and confirm both fire, in registration order, from a single .emit().
  3. Use .once() to register a listener that should only respond to the very first event of its kind, and confirm a second .emit() doesn’t trigger it again.
  4. Register a named listener function, emit the event once, remove the listener with .removeListener(), then emit again, confirming the second emit produces no output.

Recap

  • A class can extends EventEmitter to gain .on() (register a listener) and .emit() (fire an event, calling every registered listener in order).
  • .once() registers a listener that fires only the first time, then removes itself automatically.
  • .removeListener(event, namedHandler) unregisters a specific listener, requiring a named function reference to match against.

Next lesson: http, building a raw server with no framework.