Anda di halaman 1dari 11

15/02/2019 Code Your Own Event Emitter in Node.

js: A Step-by-step Guide - CodeProject

Code Your Own Event Emitter in Node.js: A Step-by-step


Guide
Rajesh Pillai, 14 Feb 2019

Understand Node internals by coding your own tiny implementation of built-in node.js event emitter from scratch.

Introduction
This is a step by step guide to how to code your own event emitter in Node.js.

If you are new to Node.js, there are many tutorials here on Medium and elsewhere. You can check out my article All About Core
Node.JS, for example.

But without further ado, let's get to the topic under discussion: “Event Emitters”. Event Emitters play a very important role in the
Node.js ecosystem.

The EventEmitter is a module that facilitates communication/interaction between objects in Node. EventEmitter is at the
core of Node asynchronous event-driven architecture. Many of Node’s built-in modules inherit from EventEmitter including
prominent frameworks like Express.js.

The concept is quite simple: emitter objects emit named events that cause previously registered listeners to be called. So, an
emitter object basically has two main features:
Emitting name events
Registering and unregistering listener functions

It’s kind of like a pub/sub or observer design pattern (though not exactly).

What We Will Be Building in This Tutorial


EventEmitter class
on / addEventListener method
off / removeEventListener method
once method
emit method
rawListeners method
listenerCount method
The above basic features are sufficient to implement a full system using the eventing model.

Before we get into the coding, let’s take a look at how we will be using the EventEmitter class. Please note that our code will
mimic the exact API of the Node.js ‘events’ module.

In fact, if you replace our EventEmitter with Node.js’s built-in ‘events’ module, you will get the same result.

Example 1 — Create an Event Emitter Instance and Register a Couple of Callbacks

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 1/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject

const myEmitter = new EventEmitter();

function c1() {
console.log('an event occurred!');
}

function c2() {
console.log('yet another event occurred!');
}

myEmitter.on('eventOne', c1); // Register for eventOne


myEmitter.on('eventOne', c2); // Register for eventOne

When the event ‘eventOne’ is emitted, both the above callbacks should be invoked.

myEmitter.emit('eventOne');

The output in the console will be as follows:

an event occurred!
yet another event occurred!

Example 2 — Registering for the Event to Be Fired Only One Time Using Once

myEmitter.once('eventOnce', () => console.log('eventOnce once fired'));

Emitting the event ‘eventOnce’:

myEmitter.emit('eventOne');

The following output should appear in the console:

eventOnce once fired

Emitting events registered with once again will have no impact.

myEmitter.emit('eventOne');

Since the event was only emitted once, the above statement will have no impact.

Example 3 — Registering for the Event With Callback Parameters

myEmitter.on('status', (code, msg)=> console.log(`Got ${code} and ${msg}`));

Emitting the event with parameters:

myEmitter.emit('status', 200, 'ok');

The output in the console will be as below:

Got 200 and ok

NOTE: You can emit events multiple times (except the ones registered with the once method).

Example 4 — Unregistering Events

myEmitter.off('eventOne', c1);

Now if you emit the event as follows, nothing will happen and it will be a noop:

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 2/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject

myEmitter.emit('eventOne'); // noop

Example 5 — Getting Listener Count

console.log(myEmitter.listenerCount('eventOne'));

NOTE: If the event has been unregistered using off or removeListener method, then the count will be 0.

Example 6 — Getting Raw Listeners

console.log(myEmitter.rawListeners('eventOne'));

Example 7 — Async Example Demo

// Example 2->Adapted and thanks to Sameer Buna


class WithTime extends EventEmitter {
execute(asyncFunc, ...args) {
this.emit('begin');
console.time('execute');
this.on('data', (data)=> console.log('got data ', data));
asyncFunc(...args, (err, data) => {
if (err) {
return this.emit('error', err);
}
this.emit('data', data);
console.timeEnd('execute');
this.emit('end');
});
}
}

Using the withTime event emitter:

const withTime = new WithTime();

withTime.on('begin', () => console.log('About to execute'));


withTime.on('end', () => console.log('Done with execute'));

const readFile = (url, cb) => {


fetch(url)
.then((resp) => resp.json()) // Transform the data into json
.then(function(data) {
cb(null, data);
});
}

withTime.execute(readFile, 'https://jsonplaceholder.typicode.com/posts/1');

Check the output in the console. The list of posts will be displayed along with other logs.

The Observer Pattern for Our Event Emitter

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 3/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject

Visual Diagram 1 (Methods in our EventEmitter)

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 4/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject

Since we now understand the usage API, let’s get to coding the module.

The Complete Boilerplate Code for the EventEmitter Class


We will be filling in the details incrementally in the next couple sections.

class EventEmitter {
listeners = {}; // key-value pair

addListener(eventName, fn) {}
on(eventName, fn) {}

removeListener(eventName, fn) {}
off(eventName, fn) {}

once(eventName, fn) {}

emit(eventName, ...args) { }

listenerCount(eventName) {}

rawListeners(eventName) {}
}

We begin by creating the template for the EventEmitter class along with a hash to store the listeners. The listeners will be
stored as a key-value pair. The value could be an array (since for the same event, we allow multiple listeners to be registered).

1. The addListener() Method


Let us now implement the addListener method. It takes in an event name and a callback function to be executed.

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 5/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject

addListener(event, fn) {
this.listeners[event] = this.listeners[event] || [];
this.listeners[event].push(fn);
return this;
}

A Little Explanation

The addListener event checks if the event is already registered. If yes, returns the array, otherwise empty array.

this.listeners[event] // will return array of events or undefined (first time registration)

For example…

Let’s understand this with a usage example. Let’s create a new eventEmitter and register a ‘test-event’. This is the first
time the ‘test-event’ is being registered.

const eventEmitter = new EventEmitter();


eventEmitter.addListener('test-event',
()=> { console.log ("test one") }
);

Inside addListener() method:

this.listeners[event] => this.listeners['test-event']


=> undefined || []
=> []

The result will be:

this.listeners['test-event'] = []; // empty array

and then the ‘fn’ will be pushed to this array as shown below:

this.listeners['test-event'].push(fn);

I hope this makes the ‘addListener’ method very clear to decipher and understand.

A note: Multiple callbacks can be registered against that same event.

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 6/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject

2. The on Method
This is just an alias to the ‘addListener’ method. We will be using the ‘on’ method more than the ‘addListener’ method for
the sake of convenience.

on(event, fn) {
return this.addListener(event, fn);
}

3. The removeListener(event, fn) Method


The removeListener method takes an eventName and the callback as the parameters. It removes said listener from the
event array.

NOTE: If the event has multiple listeners, then other listeners will not be impacted.

First, let’s take a look at the full code for removeListener.

removeListener (event, fn) {


let lis = this.listeners[event];
if (!lis) return this;

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 7/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject
for(let i = lis.length; i > 0; i--) {
if (lis[i] === fn) {
lis.splice(i,1);
break;
}
}
return this;
}

Here’s the removeListener method explained step-by-step:

Grab the array of listeners by ‘event’


If none found, return ‘this’ for chaining.
If found, loop through all listeners. If the current listener matches with the ‘fn’ parameter use the splice method of the array
to remove it. Break from the loop.
Return ‘this’ to continue chaining.

4. The off(event, fn) Method


This is just an alias to the ‘removeListener’ method. We will be using the ‘on’ method more than the ‘addListener’
method for the sake of convenience.

off(event, fn) {
return this.removeListener(event, fn);
}

5. The once(eventName, fn) Method


Adds a one-time listener function for the event named eventName. The next time eventName is triggered, this listener is
removed and then invoked.

Use for setup/init kind of events.

Let’s take a peek at the code:

once(eventName, fn) {
this.listeners[event] = this.listeners[eventName] || [];
const onceWrapper = () => {
fn();
this.off(eventName, onceWrapper);
}
this.listeners[eventName].push(onceWrapper);
return this;
}

Here’s the once method explained step-by-step:

Get the event array object. Empty array if the first time.
Create a wrapper function called onceWrapper which will invoke the fn when the event is emitted and also removes the
listener.
Add the wrapped function to the array.
Return ‘this’ for chaining.

6. The emit (eventName, ..args) Method


Synchronously calls each of the listeners registered for the event named eventName, in the order they were registered, passing
the supplied arguments to each.

Returns true if the event had listeners, false otherwise.

emit(eventName, ...args) {
let fns = this.listeners[eventName];
if (!fns) return false;
fns.forEach((f) => {

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 8/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject
f(...args);
});
return true;
}

Here’s the emit method explained step-by-step:

Get the functions for the said eventName parameter


If no listeners, return false
For all function listeners, invoke the function with the arguments
Return true when done

7. The listenerCount (eventName) Method


Returns the number of listeners listening to the event named eventName.

Here’s the source code:

listenerCount(eventName) {
let fns = this.listeners[eventName] || [];

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 9/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject
return fns.length;
}

Here’s the listenerCount method explained step-by-step:

Get the functions/listeners under consideration or an empty array if none.


Return the length.

8. The rawListeners(eventName) Method


Returns a copy of the array of listeners for the event named eventName, including any wrappers (such as those created
by .once()). The once wrappers in this implementation will not be available if the event has been emitted once.

rawListeners(event) {
return this.listeners[event];
}

The full source code for reference:

class EventEmitter {
listeners = {}

addListener(eventName, fn) {
this.listeners[eventName] = this.listeners[eventName] || [];
this.listeners[eventName].push(fn);
return this;
}

on(eventName, fn) {
return this.addListener(eventName, fn);
}

once(eventName, fn) {
this.listeners[eventName] = this.listeners[eventName] || [];
const onceWrapper = () => {
fn();
this.off(eventName, onceWrapper);
}
this.listeners[eventName].push(onceWrapper);
return this;
}

off(eventName, fn) {
return this.removeListener(eventName, fn);
}

removeListener (eventName, fn) {


let lis = this.listeners[eventName];
if (!lis) return this;
for(let i = lis.length; i > 0; i--) {
if (lis[i] === fn) {
lis.splice(i,1);
break;
}
}
return this;
}

emit(eventName, ...args) {
let fns = this.listeners[eventName];
if (!fns) return false;
fns.forEach((f) => {
f(...args);
});
return true;
}

listenerCount(eventName) {
let fns = this.listeners[eventName] || [];
return fns.length;

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 10/11
15/02/2019 Code Your Own Event Emitter in Node.js: A Step-by-step Guide - CodeProject
}

rawListeners(eventName) {
return this.listeners[eventName];
}
}

The complete code is available at:

https://jsbin.com/gibofab/edit?js,console,output

As an exercise, feel free to implement other events’ APIs from the documentation https://nodejs.org/api/events.html.

If you liked this article and want to see more similar articles, give a like.

Originally published on freecodecamp.org

NOTE: The code is optimized for readability and not for performance. Maybe as an exercise, you can optimize the code and share it
in the comment section. I haven’t tested fully for edge cases and some validations may be off as this was a quick writeup.

This article is part of the upcoming video course “Node.JS Master Class — Build Your Own ExpressJS-Like MVC Framework from
scratch”.

The title of the course is not yet finalized.

My Visual notes on twitter https://twitter.com/rajeshpillai

License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author


Rajesh Pillai
Chief Technology Officer Algorisys Technologies Pvt. Ltd.
India

Co Founder at Algorisys Technologies Pvt. Ltd.

http://algorisys.com/

Comments and Discussions


0 messages have been posted for this article Visit https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-
Emitter-in-Node-js-A-Step-by-s to post and view comments on this article, or click here to get a print view with messages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile Article Copyright 2019 by Rajesh Pillai
Web04 | 2.8.190214.1 | Last Updated 15 Feb 2019 Everything else Copyright © CodeProject, 1999-2019

https://www.codeproject.com/Articles/1277373/Code-Your-Own-Event-Emitter-in-Node-js-A-Step-by-s?display=Print 11/11

Anda mungkin juga menyukai