In this tutorial we will be exploring the built-in events module in NodeJs and particularly the EventEmitter class through a couple of practical examples. I'll start off by showing you how to create an instance of the EventEmitter and then move on to building our own Node module which inherits from the EventEmitter.

So what exactly are event emitters? In NodeJs, any object that emits an event is an instance of the EventEmitter class which exposes 2 important functionalities:

  1. The ability to trigger events using eventEmitter.emit(someEvent, optionalData)
  2. The ability to assign one or more event handlers to a specific event using eventEmitter.on(someEvent, eventHandler)

Using an Instance of EventEmitter

Let's start off with a simple example to illustrate how an instance of the EventEmitter class can be created and used to listen to and trigger events.

We'll create a function to randomly select a string from an array and trigger an event with this string as the data. A listener will be listening for that specific event and will simply print out the string it receives when the event occurs.

// require the EventEmitter from the events module
const EventEmitter = require('events').EventEmitter

// create an instance of the EventEmitter object
const eventEmitter = new EventEmitter()

// register a listener for the 'randomString' event
eventEmitter.on('randomString', function (randomStr) {
  console.log('Received the string: ' + randomStr)
})

// trigger an event called 'randomString' and send
// a randomly selected string to the listeners
eventEmitter.emit('randomString', randomString())

// simple function to randomly select a string from an array
function randomString () {
  const stringsArr = ['NodeJs', 'coligo.io', 'JavaScript', 'EventEmitters']
  return stringsArr[Math.floor(Math.random() * stringsArr.length)]
}

You'd expect to see a randomly selected string from the stringsArr printed to the console if you run this app:

Received the string: coligo.io

You're not limited to passing only a single argument to a listener. You can specify as many as you need:

...

eventEmitter.on('randomString', function (randomStr1, randomStr2) {
  console.log('String 1: ' + randomStr1 + ', String 2: ' + randomStr2)
})

eventEmitter.emit('randomString', randomString(), randomString())

...

which would print something like:

String 1: coligo.io, String 2: JavaScript

You can also specify multiple listeners to the same event:

...

eventEmitter.on('randomString', function (randomStr) {
  console.log('Listener 1 received: ' + randomStr)
})

eventEmitter.on('randomString', function (randomStr) {
  console.log('Listener 2 received: ' + randomStr)
})

eventEmitter.emit('randomString', randomString())

...

There is absolutely nothing wrong with having multiple listeners to the same event. However, it's important to keep in mind that they will be called in the order in which they were registered. In our case this will print, something like:

Listener 1 received: NodeJs
Listener 2 received: NodeJs

Extending the EventEmitter

We went over the basics with a couple of examples. However, to really make the most of the EventEmitter class within your Node modules, you will need to inherit from it.

As always, I'll show you how to do this using examples. We will be building a simple Pub/Sub system which is a great and practical use case for EventEmitters. For those of you who are not familiar with the publish-subscribe pattern here's the main idea:

A sender publishes a message (event) and subscribers can express interest in one or more of these events to receive the messages

What's important to note about a Pub/Sub system is that the publisher has no idea who is subscribed to the specific events, it just sends them out regardless of whether someone is listening or not. The subscriber also knows nothing about the publisher; all it does is listen for specific events and process the data.

If this rings a bell and you're able to connect this idea to what we've already learned about EventEmitters, great! If not, read along!

Here's what our simple Pub/Sub system will do:

  1. The publisher will generate a random string every 3 seconds using the randomString() function we created in the previous examples
  2. It will check if the random string contains any of the words: 'coligo', 'nodejs', or 'javascript'
  3. If the word 'coligo' shows up, then it will emit (publish) a 'coligo' event. Same goes for 'nodejs' and 'javascript'
  4. A listener can subscribe to any of the 3 events and handle them as desired

The module extending the EventEmitter class which we are creating will be called pubsub.js. Let's go ahead and put the above 4 requirements into code:

// pubsub.js

const EventEmitter = require('events')
const util = require('util')

// get a random string and emit the appropriate event every 3 seconds
function PubSub() {
  EventEmitter.call(this)

  const that = this
  setInterval(function () {
    var randomStr = randomString()

    if (randomStr.indexOf('coligo') > -1) {
      that.emit('coligo')
    } else if (randomStr.indexOf('nodejs') > -1) {
      that.emit('nodejs')
    } else if (randomStr.indexOf('javascript') > -1) {
      that.emit('javascript')
    }
  }, 3000)
}

// conventionally, NodeJs uses util.inherits() for inheritance
util.inherits(PubSub, EventEmitter)

// simple function to randomly select a string from an array
function randomString () {
  const stringsArr = ['nodejs', 'coligo', 'javascript', 'EventEmitters',
    'random text', 'testing events in node', 'PubSub and events at coligo']
  return stringsArr[Math.floor(Math.random() * stringsArr.length)]
}

module.exports = PubSub

The util.inherits() method inherits the prototype methods from the EventEmitter to our PubSub constructor. This basically means that PubSub now has the .on(), .emit(), etc.. methods that EventEmitter class provides.

Now, when we import the PubSub module and create a new instance of it, it will automatically inherit the properties and methods of the EventEmitter. Here's a simple example of using our new pubsub.js module:

// example_pubsub.js

const PubSub = require('./pubsub.js')

// create a new instance of our PubSub class
const simplePubSub = new PubSub()

// listen for any events
simplePubSub.on('coligo', function () {
  console.log('Received a COLIGO event!')
})

simplePubSub.on('nodejs', function () {
  console.log('Received a NODEJS event!')
})

simplePubSub.on('javascript', function () {
  console.log('Received a JAVASCRIPT event!')
})

When you run the above example_pubsub.js file, you can expect to see the a console message from the listener for which an event was triggered (every 3 seconds), for example:

Received a JAVASCRIPT event!
Received a JAVASCRIPT event!
Received a COLIGO event!
Received a NODEJS event!
Received a COLIGO event!

In reality, we wouldn't randomly be picking a string from an array and emitting an event based on it's value for a Pub/Sub system. Instead you can process real-time data from sources like Twitter streams, stocks, web analytics, weather networks, traffic systems, etc... and publish it to the interested subscribers.

The power of EventEmitters extend further than just a Pub/Sub system so I'd encourage you to explore different cases where you can leverage the benefits of loose coupling and scalability that it offers.