Skip to content

Instantly share code, notes, and snippets.

@mudge
Last active April 29, 2024 07:48
Show Gist options
  • Save mudge/5830382 to your computer and use it in GitHub Desktop.
Save mudge/5830382 to your computer and use it in GitHub Desktop.
A very simple EventEmitter in pure JavaScript (suitable for both node.js and browsers).
/* Polyfill indexOf. */
var indexOf;
if (typeof Array.prototype.indexOf === 'function') {
indexOf = function (haystack, needle) {
return haystack.indexOf(needle);
};
} else {
indexOf = function (haystack, needle) {
var i = 0, length = haystack.length, idx = -1, found = false;
while (i < length && !found) {
if (haystack[i] === needle) {
idx = i;
found = true;
}
i++;
}
return idx;
};
};
/* Polyfill EventEmitter. */
var EventEmitter = function () {
this.events = {};
};
EventEmitter.prototype.on = function (event, listener) {
if (typeof this.events[event] !== 'object') {
this.events[event] = [];
}
this.events[event].push(listener);
};
EventEmitter.prototype.removeListener = function (event, listener) {
var idx;
if (typeof this.events[event] === 'object') {
idx = indexOf(this.events[event], listener);
if (idx > -1) {
this.events[event].splice(idx, 1);
}
}
};
EventEmitter.prototype.emit = function (event) {
var i, listeners, length, args = [].slice.call(arguments, 1);
if (typeof this.events[event] === 'object') {
listeners = this.events[event].slice();
length = listeners.length;
for (i = 0; i < length; i++) {
listeners[i].apply(this, args);
}
}
};
EventEmitter.prototype.once = function (event, listener) {
this.on(event, function g () {
this.removeListener(event, g);
listener.apply(this, arguments);
});
};
@aggregate1166877
Copy link

Hello, this is actually amazing. What license do you release this under?

@mudge
Copy link
Author

mudge commented Nov 20, 2023

Hi @aggregate1166877,

This was extracted from my Promise library Pacta and, as such, is released under the BSD 3-clause license.

@mudge
Copy link
Author

mudge commented Nov 20, 2023

@undecidedapollo and @coldsilk:

There seems to be a problem with the once/removeListener function. When the event is emitted and the listener is called, it calls remove. The remove function splices the array stored at this.event[eventString] while the emit function is doing a forEach on the same array. This splice modifies the array while it is being iterated against, and causes the forEach to skip the next listener.

It has been a while since I wrote this but I believe this is why emit takes a shallow copy of the list of listeners on line 55 using slice so that the loop is unaffected by removeListener modifying the underlying events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment