Implement Event Emitter

An Event Emitter is a design pattern that allows objects to emit named events and register listeners for those events. This is fundamental to understanding how many JavaScript libraries and frameworks work.

Problem Statement

Implement a custom EventEmitter class with the following methods:

  • on(event, listener) - Register a listener for an event
  • off(event, listener) - Remove a listener for an event
  • emit(event, ...args) - Emit an event with optional arguments
  • once(event, listener) - Register a one-time listener

Solution

class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }

  off(event, listener) {
    if (!this.events[event]) return;
    
    this.events[event] = this.events[event].filter(
      l => l !== listener
    );
  }

  emit(event, ...args) {
    if (!this.events[event]) return;
    
    this.events[event].forEach(listener => {
      listener(...args);
    });
  }

  once(event, listener) {
    const onceWrapper = (...args) => {
      listener(...args);
      this.off(event, onceWrapper);
    };
    
    this.on(event, onceWrapper);
  }
}

Usage Example

const emitter = new EventEmitter();

// Register listeners
emitter.on('data', (data) => {
  console.log('Received data:', data);
});

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

// Emit events
emitter.emit('data', { message: 'Hello World' });
emitter.emit('error', new Error('Something went wrong'));

// One-time listener
emitter.once('ready', () => {
  console.log('System is ready!');
});

emitter.emit('ready'); // Will log and remove the listener
emitter.emit('ready'); // Nothing happens

Key Concepts

  1. Event Registration: Store listeners in an object with event names as keys
  2. Listener Management: Use arrays to store multiple listeners per event
  3. Event Emission: Iterate through listeners and call them with arguments
  4. One-time Listeners: Wrap the original listener to auto-remove after execution

Common Interview Questions

  • How would you handle memory leaks with event listeners?
  • What's the difference between on and once?
  • How would you implement event namespacing?
  • How would you add support for async listeners?

Related Topics

  • Observer Pattern
  • Pub/Sub Pattern
  • Node.js EventEmitter
  • React Event System