DEV Community

loading...

Node.js - The EventEmitter

meddy672 profile image Matt Eddy Updated on ・6 min read

Alt Text

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();
Enter fullscreen mode Exit fullscreen mode

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...');
});

Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode
Output
Running Simple Event Callback...
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode
Output
 Running Simple Event Callback...
 Doing work on  { id: 1, name: 'John Doe' } 
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode
Output
Running Simple Event Callback...
I received the error  oh oh
Doing work on  { id: 1, name: 'John Doe' }
Enter fullscreen mode Exit fullscreen mode

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();
    }
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

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'));
Enter fullscreen mode Exit fullscreen mode
Output
Running Simple Event Callback
I received the error oh oh
Doing work on { id: 1, name: 'John Doe' }
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (2)

pic
Editor guide
Collapse
richytong profile image
Richard Tong

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.

Collapse
meddy672 profile image
Matt Eddy Author

Thanks for your comment and glad you found it helpful.