Node.js6 min read

EventEmitter Pattern

Build event-driven applications with EventEmitter. Learn the observer pattern in Node.js.

Michael Torres
December 19, 2025
0.0k0

EventEmitter Pattern

EventEmitter is the backbone of Node.js. Almost everything uses events internally.

Basic Usage

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

emitter.on('userLoggedIn', (username) => {
  console.log(`${username} logged in`);
});

emitter.emit('userLoggedIn', 'john_doe');

Output: john_doe logged in

Real Example: Order System

const EventEmitter = require('events');

class OrderSystem extends EventEmitter {
  placeOrder(orderId, items) {
    console.log(`Processing order ${orderId}`);
    
    this.emit('orderPlaced', { orderId, items });
    
    setTimeout(() => {
      this.emit('orderShipped', { orderId });
    }, 2000);
  }
}

const orders = new OrderSystem();

orders.on('orderPlaced', (data) => {
  console.log('Order placed:', data);
});

orders.on('orderShipped', (data) => {
  console.log('Order shipped:', data);
});

orders.placeOrder('ORD-123', ['item1', 'item2']);

Output:

Processing order ORD-123
Order placed: { orderId: 'ORD-123', items: ['item1', 'item2'] }
Order shipped: { orderId: 'ORD-123' }

Real Example: File Upload Progress

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

class FileUploader extends EventEmitter {
  upload(filePath) {
    const stats = fs.statSync(filePath);
    const totalSize = stats.size;
    let uploaded = 0;
    
    const stream = fs.createReadStream(filePath);
    
    stream.on('data', (chunk) => {
      uploaded += chunk.length;
      const progress = (uploaded / totalSize) * 100;
      this.emit('progress', progress.toFixed(2));
    });
    
    stream.on('end', () => {
      this.emit('complete');
    });
  }
}

const uploader = new FileUploader();

uploader.on('progress', (percent) => {
  console.log(`Uploaded: ${percent}%`);
});

uploader.on('complete', () => {
  console.log('Upload complete!');
});

uploader.upload('large-file.zip');

One-Time Listeners

emitter.once('startup', () => {
  console.log('App started');
});

emitter.emit('startup');
emitter.emit('startup');

Output: App started (only once)

Error Handling

emitter.on('error', (err) => {
  console.error('Error occurred:', err);
});

emitter.emit('error', new Error('Something went wrong'));

Important: If you don't handle error events, Node.js will crash!

Remove Listeners

function onData(data) {
  console.log(data);
}

emitter.on('data', onData);

emitter.off('data', onData);

Multiple Listeners

emitter.on('userLoggedIn', () => {
  console.log('Send welcome email');
});

emitter.on('userLoggedIn', () => {
  console.log('Update last login time');
});

emitter.on('userLoggedIn', () => {
  console.log('Log analytics event');
});

emitter.emit('userLoggedIn');

All three listeners execute when event fires.

Key Takeaway

EventEmitter enables loose coupling between components. Extend EventEmitter for custom classes. Always handle error events. Remove listeners when done to prevent memory leaks.

#Node.js#Events#Patterns#Architecture