Skip to content

Latest commit

 

History

History
182 lines (123 loc) · 7.11 KB

16.create-an-Event-Emitter.md

File metadata and controls

182 lines (123 loc) · 7.11 KB

16. create an Event Emitter

Event emitter is a design pattern used to handle events, allowing for communication between different parts of an application. It is commonly used in Node.js but can be implemented in the browser environment as well. An event emitter allows one part of an application to emit an event and other parts to listen for and respond to that event.

Key Concepts of Event Emitters

  1. Emitting Events: When an event occurs, the emitter object generates (emits) the event. This can be done with a method like emit().

  2. Listening for Events: Other parts of the application can listen for these events and execute callback functions when the event is emitted. This can be done with methods like on() or addListener().

  3. Removing Listeners: You can also remove event listeners if they are no longer needed using methods like removeListener() or off().

Problem

https://bigfrontend.dev/problem/create-an-Event-Emitter

Problem Description

There is Event Emitter in Node.js, Facebook once had its own implementation but now it is archived.

You are asked to create an Event Emitter Class

const emitter = new Emitter();

It should support event subscribing

const sub1 = emitter.subscribe('event1', callback1);
const sub2 = emitter.subscribe('event2', callback2);

// same callback could subscribe
// on same event multiple times
const sub3 = emitter.subscribe('event1', callback1);

emit(eventName, ...args) is used to trigger the callbacks, with args relayed

emitter.emit('event1', 1, 2);
// callback1 will be called twice

Subscription returned by subscribe() has a release() method that could be used to unsubscribe

sub1.release();
sub3.release();
// now even if we emit 'event1' again,
// callback1 is not called anymore

Solution

class EventEmitter {
  constructor() {
    this.callbacks = new Map();
  }

  subscribe(eventName, callback) {
    if (!this.callbacks.has(eventName)) {
      this.callbacks.set(eventName, [callback]);
    } else {
      const cbs = this.callbacks.get(eventName);
      cbs.push(callback);
    }
    return {
      release: () => {
        const cbs = this.callbacks.get(eventName);
        const cbIndex = cbs.indexOf(callback);
        cbs.splice(cbIndex, 1);
      },
    };
  }

  emit(eventName, ...args) {
    const cbs = this.callbacks.get(eventName);
    if (!cbs.length) {
      return;
    }

    for (const cb of cbs) {
      cb.apply(this, args);
    }
  }
}

Explanation

Let's break down the EventEmitter class:

  1. Constructor: The constructor initializes a new Map object this.callbacks. This Map will store arrays of callback functions for each event name.

  2. subscribe method: This method is used to register a callback function for a specific event name. If the event name is not already in this.callbacks, it adds a new entry with the event name as the key and an array containing the callback as the value. If the event name is already in this.callbacks, it adds the callback to the existing array. The method returns an object with a release method that can be used to unsubscribe the callback.

  3. release method: This method is used to remove a callback from an event name. It finds the index of the callback in the array for the event name and removes it using Array.splice.

  4. emit method: This method is used to trigger an event. It gets the array of callbacks for the event name and calls each callback with the provided arguments.

  5. Usage: The usage example shows how to use the EventEmitter class. It creates a new EventEmitter instance, subscribes two callbacks to an event named "event", emits the "event" with the data "hello", unsubscribes the first callback, and emits the "event" again with the data "world".

This EventEmitter class provides a simple implementation of the observer pattern. It allows you to register callbacks for specific events and trigger those events with any data. The release method allows for easy unsubscription of callbacks.

Usage

const emitter = new EventEmitter();
const sub1 = emitter.subscribe("event", (data) => {
  console.log("1", data);
});

const sub2 = emitter.subscribe("event", (data) => {
  console.log("2", data);
});

emitter.emit("event", "hello");
sub1.release();
emitter.emit("event", "world");

Let's break down the usage of the EventEmitter class:

  1. Creating an EventEmitter: const emitter = new EventEmitter(); creates a new instance of the EventEmitter class.

  2. Subscribing to an Event: const sub1 = emitter.subscribe("event", (data) => { console.log("1", data); }); subscribes a callback function to the "event" event. Whenever "event" is emitted, it will log "1" followed by the data passed to the emit method. The subscribe method returns an object with a release method, which is stored in sub1.

  3. Subscribing Another Callback: const sub2 = emitter.subscribe("event", (data) => { console.log("2", data); }); subscribes another callback function to the "event" event. This callback will log "2" followed by the emitted data.

  4. Emitting an Event: emitter.emit("event", "hello"); emits the "event" event with the data "hello". This will trigger all callbacks subscribed to "event", so "1 hello" and "2 hello" will be logged.

  5. Releasing a Subscription: sub1.release(); calls the release method of the object returned by subscribe, which unsubscribes sub1's callback from the "event" event. Now, only sub2's callback is subscribed to "event".

  6. Emitting the Event Again: emitter.emit("event", "world"); emits the "event" event again, this time with the data "world". Since sub1's callback has been unsubscribed, only sub2's callback is triggered, so "2 world" is logged.

This code demonstrates how to use the EventEmitter class to subscribe to and emit events, and how to unsubscribe from events. The "event" event is just an example; you could use any string as the event name. The data passed to emit can also be anything, not just a string.

Real World Examples

Sure! Here are some real-world examples of how event emitters can be used in JavaScript:

  1. Chat Application (Node.js)

    • Handling new messages.
    • Managing user connections and disconnections.
  2. Real-Time Data Updates (Node.js)

    • Updating stock prices.
    • Real-time dashboard updates.
  3. Task Queue (Node.js)

    • Processing tasks asynchronously.
    • Handling different stages of task execution (added, processing, completed).
  4. Server Monitoring (Node.js)

    • Monitoring server status and health.
    • Triggering alerts on specific events like high CPU usage or memory leaks.
  5. File System Operations (Node.js)

    • Watching for file changes.
    • Responding to file read/write events.
  6. Notification Systems (Node.js)

    • Sending notifications based on specific triggers.
    • Managing subscription and unsubscription of notification listeners.

Reference

Facebook Event-Emitter