Skip to content

Commit

Permalink
feat: creating event-emitter
Browse files Browse the repository at this point in the history
  • Loading branch information
Shaban-Eissa committed May 30, 2024
1 parent b3bc69c commit 9ba5444
Showing 1 changed file with 175 additions and 0 deletions.
175 changes: 175 additions & 0 deletions 16.create-an-Event-Emitter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# 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](https://nodejs.org/api/events.html#events_class_eventemitter), Facebook once had [its own implementation](https://github.com/facebookarchive/emitter) but now it is archived.

You are asked to create an Event Emitter Class

```js
const emitter = new Emitter();
```

It should support event subscribing

```js
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

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

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

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

#

### Solution

```js
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
```js
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.

0 comments on commit 9ba5444

Please sign in to comment.