Overview
This article is Part 1 of Working With Node.js , a series of articles to help simplify learning Node.js. The topic which I will focus on here will be Node.js EventEmitter.
Objectives
In this article, we will learn what the Node.js EventEmitter is, and how we can use it. We will start with an introduction, then show some code examples. Finally, we'll learn some fundamental concepts about event emitters and use those concepts to build our own SimpleEmitter with just plain JavaScript.
Section 1 - About The Event Emitter
In its simplest form, the event emitter in Node.js is just a Callback. To be precise its an array of callbacks. What do we use event emitters for? We actually use them quite often. HTTP server, response and request objects all inherit the event emitter class. Event emitters allow us to incorporate an event driven design pattern on our server or application. This design pattern works very similar to a publisher and subscriber pattern, where the event emitter will publish new messages to all its listeners, and the listeners will respond and react to its caller.
Section 2 - Working With Node.js Event Emitter
Creating an event emitter with Node.js is pretty straight forward. The event emitter is a class within the events module, therefore it must be required.
index.js
const { EventEmitter } = require('events');
const simpleEmitter = new EventEmitter();
Once we have the emitter imported, we can create a listener and publish messages to the listener. To add listeners we use the on
method of the event emitter followed by the name of the event. You can name your events whatever like as it is only used to identify what listeners will execute when a message is published or sent. I have named my listener my-simple-event.
index.js
const { EventEmitter } = require('events');
const simpleEmitter = new EventEmitter();
simpleEmitter.on('my-simple-event', () => {
console.log('Running Simple Event Callback...');
});
As you can see the on
method takes the name of the event to listen to, and a function to run once we send a message to it. Now, we just need to publish a message to that event. We can do that with the emit
method followed by the name of the event. Putting it all together
index.js
const { EventEmitter } = require('events');
const simpleEmitter = new EventEmitter();
simpleEmitter.on('my-simple-event', () => {
console.log('Running Simple Event Callback...');
});
simpleEmitter.emit('my-simple-event');
Output
Running Simple Event Callback...
Once a message is published with the emit
method, the listener will run the callback function. In its simplest form, this is the Node.js event emitter in action. You can create as many events as you like for as many listeners as you need. You also can supply arguments to the callback function of the listener by passing additional arguments to the emit
method. Lets do that now. I'll create another listener called another-simple-event and I'll pass an object to it using the emit
method.
index.js
const { EventEmitter } = require('events');
const simpleEmitter = new EventEmitter();
const user = { id: 1, name: 'John Doe'}
simpleEmitter.on('my-simple-event', () => {
console.log('Running Simple Event Callback...');
});
simpleEmitter.on('another-simple-event', (user) => {
console.log('Doing work on', user);
});
simpleEmitter.emit('my-simple-event');
simpleEmitter.emit('another-simple-event', user);
Output
Running Simple Event Callback...
Doing work on { id: 1, name: 'John Doe' }
The event emitter class within Node.js has a ton of built-in methods that you can use right out of the box. Below are some commonly used methods.
- on - will register a listener for that event.
- emit - will publish or send new messages to it's listeners.
- once - will run only once and discontinue listening to further messages.
- off - removes all listeners from an event.
Now, no code is complete without some error handling. If an error occurs while an event is being published then the process will crash. To avoid this you can register an error listener to handle errors. Lets do that now. I'll add a third listener called error which will be used to handle errors for the simpleEmitter
. I'll emit the error before I publish a message to the another-simple-event listener.
index.js
const { EventEmitter } = require('events');
const simpleEmitter = new EventEmitter();
const user = { id: 1, name: 'John Doe'}
simpleEmitter.on('my-simple-event', () => {
console.log('Running Simple Event Callback...');
});
simpleEmitter.on('another-simple-event', (user) => {
console.log('Doing work on', user);
});
simpleEmitter.on('error', (err) => {
console.error('I received the error ',err.message);
});
simpleEmitter.emit('my-simple-event');
simpleEmitter.emit('error', new Error('oh oh'));
simpleEmitter.emit('another-simple-event', user);
Output
Running Simple Event Callback...
I received the error oh oh
Doing work on { id: 1, name: 'John Doe' }
If you remove the error listener and run the program again the process will crash and another-simple-event will not publish a message to its listener.
Section 3 - Building An Event Emitter With Just JavaScript
As I stated before, the event emitter is just an array of callbacks. Let me show you what I mean with the code snippet below.
Code snippet
const mySimpleEvent = [
function () { console.log('Hello') },
function (user) { console.log(user) },
function () { console.log('World') }
];
mySimpleEvent.forEach((fn, index) => {
if (index === 1) {
const user = { id: 1, name: 'John Doe' }
fn(user);
} else {
fn();
}
});
In the code snippet above, I created an array that contains three functions where each function will do something different. Next, I loop through the array calling each function and for the second index of the array, I pass in a user object. If you run the code above you will receive the output below.
Output
Hello
{ id: 1, name: 'John Doe' }
World
From the code snippet above, we can derive some simple fundamental concepts about the event emitter and thus event driven development. If we have an application that is defined by a set of use cases or events, then we can design our application to work based on those events. Now, in regards to the mySimpleEvent
from the code snippet above, each function represents a listener that is attached to mySimpleEvent
. From there, all we have to do is loop through mySimpleEvent
and call each listener. With this knowledge we can build are own event emitter class, lets do that now. I'll create another file called SimpleEmitter.js. This class will have two methods on
and emit
just like the those of Node.js's event emitter class.
SimpleEmitter.js
class SimpleEmitter {
constructor() {
this.event = {};
}
on(eventName, listener) {
if (!this.event[eventName]) {
this.event[eventName] = [];
}
return this.event[eventName].push(listener);
}
emit(eventName, data) {
if (!this.event[eventName]) {
return;
}
this.event[eventName].forEach((cb) => {
cb(data);
});
}
}
Lets take a moment to understand the SimpleEmitter class. Each time we call the on
method we check to see if the event name is stored within our event object. If the event name is not found, we create a key for it to reference an array of listeners. Once the emit
method is called, it will check for the event name and if not found the method will end there. If it does find the event name then it will loop through the array and call each listener. Now we just need to use the SimpleEmitter class as we did before. Putting it all together
SimpleEmitter.js
class SimpleEmitter {
constructor() {
this.event = {};
}
on(eventName, listener) {
if (!this.event[eventName]) {
this.event[eventName] = [];
}
return this.event[eventName].push(listener);
}
emit(eventName, data) {
if (!this.event[eventName]) {
return;
}
this.event[eventName].forEach((cb) => {
cb(data);
});
}
}
const myEmitter = new SimpleEmitter();
const user = { id: 1, name: 'John Doe' };
myEmitter.on('my-simple-event', () => {
console.log('Running Simple Event Callback');
});
myEmitter.on('another-simple-event', (user) => {
console.log('Doing work on', user);
});
myEmitter.on('error', (err) => {
console.log('I received the error',err.message);
})
myEmitter.emit('my-simple-event');
myEmitter.emit('another-simple-event', user);
myEmitter.emit('error', new Error('oh oh'));
Output
Running Simple Event Callback
I received the error oh oh
Doing work on { id: 1, name: 'John Doe' }
The EventEmitter class within Node.js covers more complex use cases within its application code. However, the fundamentals are the same. Thank you for taking time to read this article and if you found it helpful please leave a rating. If you have a question please post it in the discussion below.
Top comments (4)
Very nice, clear, and simple explanation of Node.js EventEmitter. Also, I appreciate your examples - they are simple and easy to follow as well. Thanks for this article.
Thanks for your comment and glad you found it helpful.
Very impressive. Loved how you navigate step by step from simple to intermediate cases with examples.
Thank you. Glad you found this article.