Events using EventEmitter

Events Overview

Another way to invoke asynchronous non-blocking code is by using Event model, apart from the callback functions. Event model follows a publish and subscribe approach.

You can have multiple functions subscribed to a published event, so that when that event occurs all those functions that have subscribed get executed automatically.

EventEmitter Class

Node.js provides EventEmitter class which we can use to build event driven interfaces. This class is present within events module.

Subscribing for an Event

The code that wants to get subscribed for events, will be passed into the “on” function of the EventEmitter instance, along with the name of the event.

EventEmitter.on(event_name, listener);

Publishing an Event

The code that publishes the events call emit function and specifies the name of the event that is being emitted.

EventEmitter.emit(event_name, [arguments]);

The first argument of emit function is the name of the event. We can call the emit function with zero or more arguments right after event name. These arguments will be passed as parameters to the functions that have subscribed for this event.

Working with EventEmitter

There are 3 different ways in which you can publish and subscribe for events using EventEmitter.

  1. Directly creating an instance of EventEmitter
  2. Inheriting the prototype methods of EventEmitter using util.inherits
  3. Inheriting EventEmitter using ES6 class and extends keywords

Instantiating EventEmitter

In this approach, we create an instance of EventEmitter in a function. This function publishes 3 events by emitting events at logical endpoints of the program.

It emits “start” event when the function begins execution, “fetch” event for each user record and “end” event after completion of fetching the records. You can see that when it emits “fetch” event it sends the record as the second argument to emit function.

At the end of the function, it returns EventEmitter object. We use this object to subscribe event listeners using the “on” methods. The “fetch” listener receives the published user record.

var EventEmitter = require("events").EventEmitter;

var getUsers = function() {
  let emitter = new EventEmitter();
  emitter.emit("start");
  process.nextTick(function() {
    let mockUsers = [
      ["U001", "John Doe", "john.doe@techstackjournal.com"],
      ["U002", "Jane Doe", "jane.doe@techstackjournal.com"]
    ];
    mockUsers.forEach(user => emitter.emit("fetch", user));
    emitter.emit("end");
  });
  return emitter;
};

var emitter = getUsers();

emitter.on("start", function() {
  console.log("getUsers Started");
});

emitter.on("fetch", function(user) {
  console.log(user);
});

emitter.on("end", function(t) {
  console.log("getUsers Completed.");
});

console.log("Event and listeners initialized...");

In this example, we’ve used process.nextTick to emulate the asynchronous operation. On the very next tick of the Event Loop, it executes this function. That means Event Loop executes this function only after executing the last statement in our program.

Inheriting EventEmitter using util.inherits

In this approach, we inherit the prototype methods of EventEmitter into UserService using util.inherits. We then use the instance of UserService to subscribe for events. Within UserService we emit events using the reference of this operator as if UserService is an EventEmitter even before UserService inherits prototype methods of EventEmitter using util.inherits method.

var EventEmitter = require("events").EventEmitter;
var util = require("util");

var UserService = function() {
  let self = this;
  process.nextTick(function() {
    let mockUsers = [
      ["U001", "John Doe", "john.doe@techstackjournal.com"],
      ["U002", "Jane Doe", "jane.doe@techstackjournal.com"]
    ];
    self.emit("start");
    mockUsers.forEach(user => self.emit("fetch", user));
    self.emit("end");
  });
};

util.inherits(UserService, EventEmitter);

var emitter = new UserService();

emitter.on("start", function() {
  console.log("getUsers Started");
});

emitter.on("fetch", function(user) {
  console.log(user);
});

emitter.on("end", function(t) {
  console.log("getUsers Completed.");
});

console.log("Event and listeners initialized...");

Inheriting EventEmitter using ES6 Class

In this approach, we’ll create an ES6 class extending from EventEmitter. Within the ES6 class we emit events using the reference of this operator. We create an instance of that ES6 class to subscribe for the events.

const EventEmitter = require("events");

class UserService extends EventEmitter {
  traverse() {
    let self = this;
    process.nextTick(function(){
      let mockUsers = [
        ["U001", "John Doe", "john.doe@techstackjournal.com"],
        ["U002", "Jane Doe", "jane.doe@techstackjournal.com"]
      ];
      self.emit("start");
      mockUsers.forEach(user => self.emit("fetch", user));
      self.emit("end");
    });
  }
}

const service = new UserService();

service.on("start", function() {
  console.log("traverse started...");
});

service.on("fetch", function(user) {
  console.log(user);
});

service.on("end", function() {
  console.log("traverse ends...");
});

service.traverse();

console.log("Event and listeners initialized...");

Built-in Classes that Inherit EventEmitter

Certain built-in classes in Node.js emit events based on the actions or its state. For example, http.Server class inherits from EventEmitter and it emits 'request' event. Similarly, fs.ReadStream class also inherits from EventEmitter and emits 'data' event.

fs.ReadStream and http.Server inherit from EventEmitter

Simple Web Application using http.Server

Let’s see a simple example of http.Server which extends EventEmitter. We first create an instance of http.Server using http.createServer function. We call the listen method on this server object to listen for server requests on port 8080.

language-jsvar http = require("http");

var server = http.createServer();

server.on("request", (request, response) => {
  response.end("Good Morning!!!");
  console.log(request.url);
});

server.on("close", () => {
  console.log("Bye!!!");
});

process.on("SIGINT", () => {
  server.close();
});

server.listen(8080);

console.log("Server started. Hit http://localhost:8080");

We then subscribe for server events request and close. When the request event emits, it will pass on the request and response objects to its listeners or subscribers. Using response.end method we are returning a “Good Morning” message to the browser. Event Loop will raise close event on server when the server goes down. Our subscribed listener listen the close event and logs “Bye!!!”.

We are also subscribing for “SIGINT” event on process object. When we press Ctrl+C to terminate the server, a “SIGINT” signal will be raised. When SIGINT signal is raised, we are calling server.close to kill the server.