EventEmitter Invokes Event Callbacks In The Context Of The EventEmitter Instance In Node.js
This is a small but powerful Node.js feature. When you bind an event handler to an EventEmitter instance (or a sub-class instance such as Readable Stream), that callback gets invoked in the context of that EventEmitter instance. This means that, unless you are explicitly overriding the context with .bind(), your event handler can always make use of "this" if it needs to reference the EventEmitter instance.
That's a bit of a mouth-full, so let's look at some code. In the following demo, I'm sub-classing the EventEmitter module. Then, I'm going to bind to the "stop" event both internally and externally. In both cases, I'm going to check to see if the current "this" scope references the instantiated EventEmitter sub-class:
// Require module references.
var events = require( "events" );
var util = require( "util" );
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //
// I sub-class the EventEmitter.
function Session() {
// Call the super constructor.
events.EventEmitter.call( this );
// Since this is a sub-class of the EventEmitter, we can pass in the raw method
// reference without worrying about the binding. When EventEmitter invokes the
// callback, it will call it in the context of THIS, which is the owner of the
// method in question to begin with.
this.on( "stop", this._destroy );
}
util.inherits( Session, events.EventEmitter );
// I tear-down the instance.
Session.prototype._destroy = function() {
// NOTE: Since this method is being called as part of a .on() event binding,
// this *should* be the current object instance.
console.log( "Destroy: (this === session : %s)", ( this === session ) );
};
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //
var session = new Session();
// When the stop event takes place, compare the execution context to our session
// instance. EventEmitter should invoke this in the context of the EventEmitter instance.
session.on(
"stop",
function handleStop() {
console.log( "Stop: (this === session : %s)", ( this === session ) );
}
);
// Trigger the event.
session.emit( "stop" );
The part of particular interest is the fact that, internally, I can bind to my own events using raw method references:
this.on( "stop", this._destroy );
If you're used to dealing with callback in the context of a function like setTimeout() or setInterval(), you might think that this line is going to cause buggy behavior since we're passing around a free-floating method reference without binding it to a particular scope. But, since EventEmitter invokes callbacks in the context of the EventEmitter instance, we get the following terminal output when we run the code:
Destroy: (this === session : true)
Stop: (this === session : true)
As you can see, in both cases, the "this" scope refers to the instantiated EventEmitter sub-class, "session".
Like I said, this is a really small Node.js feature; but, it's pretty cool and should simplify code that has to deal with EventEmitter. Furthermore, if you can define an event handler without referencing variables in the closure created by the callback, it should make garbage collection easier as it reduces memory references (although that last part is just my theory).
Want to use code from this post? Check out the license.
Reader Comments