Code Review: Oliver Caldwell's EventEmitter
Code Review is a series on DailyJS where I take a look at an open source project to see how it’s built. Along the way we’ll learn patterns and techniques by JavaScript masters. If you’re looking for tips to write better apps, or just want to see how they’re structured in established projects, then this is the tutorial series for you.
After a previous Code Review of EventEmitter2 by hij1nx in Code Review: EventEmitter2, Oliver Caldwell asked me to take a look at his EventEmitter (GitHub: Wolfy87 / EventEmitter, License: MIT and GPL).
This library places special focus on browser support, supporting all major browsers and even IE 5+. A minimised version is also supplied to further support client-side developers.
Usage
This EventEmitter implementation is used the same way as Node’s:
// Initialise the EventEmitter
var ee = new EventEmitter();
function myListener() {
}
// Add the listener
ee.addListener('event', myListener);
// Emit the event
ee.emit('event');
// Remove the listener
ee.removeListener('event', myListener);
Structure
As with most browser-based libraries, I expected to see a self-executing anonymous function. However, in this implementation the author has opted to wrap everything in a regular function, declaring methods inside the function’s scope:
function EventEmitter() {
var listeners = {},
instance = this;
instance.Event = function(type, listener, scope, once) {
};
instance.eachListener = function(type, callback) {
};
// Etc.
}
The Event class is used to encapsulate an event, and instantiated Event objects are pushed onto arrays indexed by type on listeners.
This style of encapsulation reminds me of how TJ Holowaychuk likes to model his runtime objects (the Request and Response classes in Express for example). Also, it demonstrates high-level object oriented principles without resorting to creating classical-OO-style classes in JavaScript.
Implementation
Like the other EventEmitter libraries, listeners are stored in an Object. The newListener event has been retained, which I thought shows good attention to detail. Most of the core methods are extremely short, which I like to see. For example, emit is implemented like this:
instance.emit = function(type, args) {
instance.eachListener(type, function(currentListener) {
return currentListener.fire(args);
});
// Return the instance to allow chaining
return instance;
};
Extracting eachListener seems like a good idea. Rather than having a lot of loops throughout the code, eachListener gets reused by removeListener and emit, making these methods more readable.
The code is also clearly commented, and the build process is included as part of the Makefile.
Performance
The last time I looked at serious JavaScript loop benchmarks, optimised for loops performed extremely well, which I suspect is why EventEmitter and EventEmitter2 don’t use the callback-based iterators Oliver has used. He hasn’t included benchmarks so I can’t easily say how well this implementation performs, but I did find this code easier to follow than the other EventEmitters that I’ve looked at.
Tests
Oliver has included QUnit tests, which do the job admirably:
test('Emitting events', function() {
var ee = new EventEmitter();
ee.addListener('emittingTest', function() {
// Listener
ok(true, 'First called');
});
ee.addListener('emittingTest', function() {
// Another listener for the same event
ok(true, 'Second called');
});
ee.addListener('differentEvent', function() {
// Another listener for the same event
ok(false, 'Wrong event called');
});
ee.emit('emittingTest');
});
I think these tests could benefit from being able to assert on how many assertions are expected before the test completes, like assert.expect in nodeunit.
Conclusion
Yet another EventEmitter implementation, but this one still manages to differentiate itself from the others I’ve looked at. I liked the way the author has explored readability rather than purely performance; it offers a hackable alternative that might suit certain projects. And the code uses OO concepts without feeling too heavy handed.
Check it out yourselves on GitHub: Wolfy87 / EventEmitter