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).
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.
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
function c1() {
console.log('an event occurred!');
}
function c2() {
console.log('yet another event occurred!');
}
When the event ‘eventOne’ is emitted, both the above callbacks should be invoked.
myEmitter.emit('eventOne');
an event occurred!
yet another event occurred!
Example 2 — Registering for the Event to Be Fired Only One Time Using Once
myEmitter.emit('eventOne');
myEmitter.emit('eventOne');
Since the event was only emitted once, the above statement will have no impact.
NOTE: You can emit events multiple times (except the ones registered with the once method).
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
console.log(myEmitter.listenerCount('eventOne'));
NOTE: If the event has been unregistered using off or removeListener method, then the count will be 0.
console.log(myEmitter.rawListeners('eventOne'));
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.
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
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.
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).
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.
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.
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.
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);
}
NOTE: If the event has multiple listeners, then other listeners will not be impacted.
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;
}
off(event, fn) {
return this.removeListener(event, fn);
}
once(eventName, fn) {
this.listeners[event] = this.listeners[eventName] || [];
const onceWrapper = () => {
fn();
this.off(eventName, onceWrapper);
}
this.listeners[eventName].push(onceWrapper);
return this;
}
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.
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;
}
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;
}
rawListeners(event) {
return this.listeners[event];
}
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);
}
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];
}
}
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.
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”.
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
http://algorisys.com/
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