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
-
Emitting Events: When an event occurs, the emitter object generates (emits) the event. This can be done with a method like emit().
-
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().
-
Removing Listeners: You can also remove event listeners if they are no longer needed using methods like removeListener() or off().
https://bigfrontend.dev/problem/create-an-Event-Emitter
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
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);
}
}
}
Let's break down the EventEmitter
class:
-
Constructor: The constructor initializes a new Map object
this.callbacks
. This Map will store arrays of callback functions for each event name. -
subscribe
method: This method is used to register a callback function for a specific event name. If the event name is not already inthis.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 inthis.callbacks
, it adds the callback to the existing array. The method returns an object with arelease
method that can be used to unsubscribe the callback. -
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 usingArray.splice
. -
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. -
Usage: The usage example shows how to use the
EventEmitter
class. It creates a newEventEmitter
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.
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:
-
Creating an EventEmitter:
const emitter = new EventEmitter();
creates a new instance of theEventEmitter
class. -
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 theemit
method. Thesubscribe
method returns an object with arelease
method, which is stored insub1
. -
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. -
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. -
Releasing a Subscription:
sub1.release();
calls therelease
method of the object returned bysubscribe
, which unsubscribessub1
's callback from the "event" event. Now, onlysub2
's callback is subscribed to "event". -
Emitting the Event Again:
emitter.emit("event", "world");
emits the "event" event again, this time with the data "world". Sincesub1
's callback has been unsubscribed, onlysub2
'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.
Sure! Here are some real-world examples of how event emitters can be used in JavaScript:
-
Chat Application (Node.js)
- Handling new messages.
- Managing user connections and disconnections.
-
Real-Time Data Updates (Node.js)
- Updating stock prices.
- Real-time dashboard updates.
-
Task Queue (Node.js)
- Processing tasks asynchronously.
- Handling different stages of task execution (added, processing, completed).
-
Server Monitoring (Node.js)
- Monitoring server status and health.
- Triggering alerts on specific events like high CPU usage or memory leaks.
-
File System Operations (Node.js)
- Watching for file changes.
- Responding to file read/write events.
-
Notification Systems (Node.js)
- Sending notifications based on specific triggers.
- Managing subscription and unsubscription of notification listeners.