Node.js EventEmitter
All asynchronous I/O operations in Node.js send an event to the event queue when they are completed.
Many objects within Node.js dispatch events: a net.Server
object triggers an event each time a new connection is made, and a fs.readStream
object triggers an event when the file is opened. All these objects that generate events are instances of events.EventEmitter
.
EventEmitter Class
The events
module provides only one object: events.EventEmitter
. The core of EventEmitter
is the encapsulation of event triggering and event listener functionality.
You can access this module by requiring "events"
:
// Import the events module
var events = require('events');
// Create an EventEmitter object
var eventEmitter = new events.EventEmitter();
If an error occurs during the instantiation of an EventEmitter
object, an error
event is triggered. When a new listener is added, the newListener
event is triggered, and when a listener is removed, the removeListener
event is triggered.
Here is a simple example demonstrating the usage of EventEmitter
:
// event.js file
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
console.log('some_event event triggered');
});
setTimeout(function() {
event.emit('some_event');
}, 1000);
The execution result is as follows:
Running this code will output 'some_event event triggered' to the console after 1 second. The principle is that the event
object registers a listener for the some_event
event, and then we use setTimeout
to send the some_event
event to the event
object after 1000 milliseconds, which in turn calls the listener for some_event
.
$ node event.js
some_event event triggered
Each event in EventEmitter
consists of an event name and several parameters. The event name is a string, usually conveying some semantics. For each event, EventEmitter
supports multiple event listeners.
When an event is triggered, the event listeners registered to this event are called sequentially, and the event parameters are passed as arguments to the callback functions.
Let's explain this process with the following example:
// event.js file
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener1', arg1, arg2);
});
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener2', arg1, arg2);
});
emitter.emit('someEvent', 'arg1 parameter', 'arg2 parameter');
Executing the above code results in the following:
$ node event.js
listener1 arg1 parameter arg2 parameter
listener2 arg1 parameter arg2 parameter
In this example, emitter
registers two event listeners for the someEvent
event and then triggers the someEvent
event.
The result shows that the two event listener callback functions are called in sequence. This is the simplest usage of EventEmitter
.
EventEmitter
provides several properties, such as on and emit. The on function is used to bind event functions, and the emit property is used to trigger an event. Next, let's look at the properties of EventEmitter
in detail.
Methods
No. | Method & Description |
---|---|
1 | addListener(event, listener) <br>Adds a listener to the end of the listeners array for the specified event. |
2 | on(event, listener) <br>Registers a listener for a specified event, accepting a string event and a callback function. server.on('connection', function (stream) {<br> console.log('someone connected!');<br>}); |
3 | once(event, listener) <br>Registers a one-time listener for a specified event, which will only be triggered once and then removed. server.once('connection', function (stream) {<br> console.log('Ah, we have our first user!');<br>}); |
4 | removeListener(event, listener) <br>Removes a specific listener for a specified event, which must be a listener that has already been registered. It accepts two parameters, the first being the event name and the second being the callback function name. var callback = function(stream) {<br> console.log('someone connected!');<br>};<br>server.on('connection', callback);<br>// ...<br>server.removeListener('connection', callback); |
5 | removeAllListeners([event]) <br>Removes all listeners for all events, or removes all listeners for a specified event if provided. |
6 | setMaxListeners(n) <br>By default, EventEmitters will output a warning if more than 10 listeners are added.<br>The setMaxListeners function is used to change the default limit for the number of listeners. |
7 | listeners(event) <br>Returns an array of listeners for a specified event. |
8 | emit(event, [arg1], [arg2], [...]) <br>Executes each listener in order, returning true if the event has registered listeners, otherwise false. |
Class Methods
No. | Method & Description |
---|---|
1 | listenerCount(emitter, event) <br>Returns the number of listeners for a specified event. |
events.EventEmitter.listenerCount(emitter, eventName) //Deprecated, not recommended
events.emitter.listenerCount(eventName) //Recommended
Events
No. | Event & Description |
---|---|
1 | newListener <br> event - String, event name<br>listener - Function handling the event This event is triggered when a new listener is added. |
2 | removeListener <br> event - String, event name<br>listener - Function handling the event Removes a listener from the specified listener array. Note that this operation will change the index of listeners that follow the removed listener. |
Example
The following example demonstrates the application of the EventEmitter class using the connection event.
Create a main.js file with the following code:
var events = require('events');
var eventEmitter = new events.EventEmitter();
// Listener #1
var listener1 = function listener1() {
console.log('Listener listener1 executed.');
}
// Listener #2
var listener2 = function listener2() {
console.log('Listener listener2 executed.');
}
// Bind the connection event with the listener1 function
eventEmitter.addListener('connection', listener1);
// Bind the connection event with the listener2 function
eventEmitter.on('connection', listener2);
var eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " listeners listening to the connection event.");
// Handle the connection event
eventEmitter.emit('connection');
// Remove the listener1 function that was bound
eventEmitter.removeListener('connection', listener1);
console.log("listener1 is no longer being listened to.");
// Trigger the connection event
eventEmitter.emit('connection');
eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " listener(s) listening to the connection event.");
console.log("Program execution complete.");
The above code, when executed, will produce the following output:
$ node main.js
2 listener(s) listening to the connection event.
listener1 executed.
listener2 executed.
listener1 is no longer being listened to.
listener2 executed.
1 listener(s) listening to the connection event.
Program execution complete.
The error Event
EventEmitter defines a special event called 'error', which carries the semantics of an error. We typically trigger the 'error' event when encountering an exception.
When 'error' is triggered, EventEmitter stipulates that if there is no corresponding listener, Node.js will treat it as an exception, exit the program, and output the error message.
We generally need to set up a listener for objects that can trigger the 'error' event to avoid the entire program crashing upon encountering an error. For example:
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
Running this will display the following error:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Uncaught, unspecified 'error' event.
at EventEmitter.emit (events.js:50:15)
at Object.<anonymous> (/home/byvoid/error.js:5:9)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Array.0 (module.js:479:10)
at EventEmitter._tickCallback (node.js:192:40)
Inheriting EventEmitter
Most of the time, we do not use EventEmitter directly but rather inherit it in objects. Core modules that support event responses, such as fs, net, and http, are subclasses of EventEmitter.
Why do we do this? There are two reasons:
Firstly, implementing events in an object with specific functionality is semantically appropriate; the listening and triggering of events should be methods of an object.
Secondly, JavaScript's object mechanism is prototype-based and supports partial multiple inheritance. Inheriting EventEmitter does not disrupt the existing inheritance relationship of the object. ```