Home

Event Emitters

According to the Node Docs...
"Much of the Node.js core API is built around an idiomatic asynchronous event-driven architecture in which certain kinds of objects (called "emitters") emit named events that cause Function objects ("listeners") to be called."

Initialize an Event Emitter

Here's 3 ways to initialize the Event Emitter:

// NOTE: this default export from "events" is not really recommended by the node docs
const { EventEmitter } = require('events')


class MyEmitter extends EventEmitter {
  constructor(opts = {}) {
    super(opts);
    this.name = opts.name;
  }
}
const newE = new MyEmitter()
myEmitter.on('dog', (d) => console.log('on dog'));
myEmitter.emit('dog');

const lastEv = new EventEmitter();
lastEv.on('water', (d) => console.log('on water'));
lastEv.emit('water');

TLDR: A Trivial Example

A trivial non-functional example, briefly illustrating the eventEmitter in action

const { EventEmitter } = require("events");

// initialize
const dataHandler = new EventEmitter();

/*
  two functions
*/ 
function cleanUpTheData(arr){
  const cleaned = arr.filter(d => d)
  dataHandler.emit('sendData',cleaned)
}

function sendData(arr){
  console.log('do something with the data here')
  console.log(arr)
}

/*
  register some events to handle
*/ 
dataHandler.on('cleanUp', cleaUpTheData)
dataHandler.on('sendData', sendData)

// kick off the logic here!
dataHandler.emit('cleanUp', [1,'apple',null,undefined,321])

The parts of the events module to start with -

  • EventEmitter: the heart of the consumable event module
  • on: creating event "handlers"
  • emit: calling an event by name

The primary goals that this doc will cover are to

  • create an Event-Managing object that
    • registers events, within a program, to "listen" for other parts of the program to emit the same event
    • emit events, by name, with data passed along, to trigger the event listener to respond

An Overview of the Events Module

There is a core node module called events which is the source for making events. This module includes a few parts:

# in a node repl
> const e = require('events')
Object.keys(e)
[
  'once',
  'on',
  'getEventListeners',
  'EventEmitter',
  'usingDomains',
  'captureRejectionSymbol',
  'captureRejections',
  'errorMonitor',
  'defaultMaxListeners',
  'setMaxListeners',
  'init',
  'listenerCount'
]

Listening For Events

on

The most-used event listener registration may be the on method.
With the eventEmitter object, the on is like describing what happens when an event gets "called" or "emitted" or "triggered".

dataHandler.on("event-name", handleParamsFn);
dataHandler.on("event-name", handleParamsFnTwo);
  • the ORDER of emitting && listening matters...
    • if emit is written before the listener, the listener will not catch the emit
  • multiple fns can happen on an even, above the handleParamsFn and handleParamsFnTwo both happen

prependListener

  • a method on an instance of an eventEmitter
  • registers at the beginning of the listeners array for the given event
  • can be written out-of-order, and will ALWAYS 'handle' an event even when the .on is written after prependListener
const { EventEmitter } = require('events');
const ee = new EventEmitter();
const EVENT_NAME = 'example';
function onEvent() {
  console.log('on the event!');
}

function onPrepend() {
  console.log('on prepend');
}
console.log('1');
ee.on(EVENT_NAME, onEvent);

ee.prependListener(EVENT_NAME, onPrepend);

ee.emit(EVENT_NAME);
console.log('2');
ee.emit(EVENT_NAME);
console.log('3');

This will log

1
on prepend
on the event!
2
on prepend
on the event!
3

once

function singleHandle(){
  console.log('single handler was called!')
}
dataHandler.once("single-event-instance-handler", singleHandler);
dataHandler.emit("single-event-instance-handler");
dataHandler.emit("single-event-instance-handler");
  • the event removes itself after it is called once
  • emitting the even again will do nothing && throw no error

Emitting Events

Emitting events might be the "easiest" part.

  • use the eventEmitter instance
  • use the emit method
  • pass the emit method 2 things:
    • a string of the event name
    • an optional payload of data for the event handler to use
dataHandler.emit("event-name", { ...eventParams });

Removing Event Listeners

removeListener

  • used to remove a listener that has been registered
  • can take 2 args/params
    • event name
    • event listener fn
dataHandler.removeListener("event-name", handleParamsFnTwo);

The 2nd arg can be one of any functions that may be registered with the event. I.E ->

dataHandler.on("event-name", firstFn);
dataHandler.on("event-name", secondFn);
dataHandler.removeListener("event-name", secondFn);

removeAllListeners

dataHandler.removeAllListeners("event-name");

Error Events

As the node docs read, "...If an EventEmitter does not have at least one listener registered for the 'error' event, and an 'error' event is emitted, the error is thrown, a stack trace is printed, and the Node.js process exits....As a best practice, listeners should always be added for the 'error' events."

Writing Events? Write an error event handler.

const { EventEmitter } = require("events");
const thisTry = new EventEmitter();

// keep process alive
process.stdin.resume();

thisTry.on("error", (err) => {
  console.log("got error:", err.message);
});

thisTry.emit("error", new Error("dummy err message"));

Events Can Be Chained

Event methods can be chained:

  • .on()
  • .once()
  • .prependListener()
  • .prependOnceListener()
  • .removeAllListeners()
  • .removeListener()
  • .setMaxListeners()

Some Take-Aways

  • EventEmitters can be seen with a few primary tools
    • the .on method, for describe an event and what to do when the event is emitted/called/triggered
    • the .emit method, for emitted/calling/triggering an event by name, and optionally passing a payload of data
    • the special case of the error event should always ve taken care of in code, explicitly
Tags: