Search code examples
javascriptevent-handlingiterationeventemitter

Creating particular loop behavior


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 EventEmitters handle this?


Solution

  • 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.