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 moduleon
: 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
andhandleParamsFnTwo
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
- the