While creating an internal EventEmitter
for my project, I became curious how to produce a particular loop iteration behavior for attached event handlers. Here's a demonstration of my concern:
MyEventEmitter.prototype.emit = function (name, event) {
var index;
if (name in this.events) {
for (index = 0; index < this.events[name].length; index++) {
this.events[name][index].call(this, event);
}
}
};
To clarify, this.events
is an object with each key being an event name and each value being an array of event handlers to be called for the respective event.
However, as I observed with Unexpected behavior of Array.prototype.splice, this would fail for event handlers attached with MyEventEmitter.prototype.once
because the wrapper event handler function would remove itself, causing the iteration to skip the event handlers within the indeces of the array immediately after the one-time event handlers in the array. Here's what I mean:
var array = [1, 2, 3, 4, 5], index;
for (index = 0; index < array.length; index++) {
// log event handlers that get called
console.log(array[index]);
// let's assume that `3` is like an event handler that removes itself
if (array[index] === 3) {
array.splice(index, 1);
}
}
This would log the following:
1
2
3
5
Note that 4
does not get iterated because 3
removes itself. How would I write emit
to accomodate this? Or, if appropriate, how do native implementations of EventEmitter
s handle this?
After looking at the node source myself, I found their solution in events.js:
listeners = handler.slice();
Basically, it makes a copy of the listeners array to iterate over so that if a listener deletes itself from the original array, the copy's indeces remain unaffected.