Event Emitter With Once And Off Semantics
A small event emitter that adds one-shot listeners, removal by original callback, snapshot dispatch and listener counts without Node dependencies.
Code
export class EventEmitter {
#listeners = new Map();
on(event, listener) {
if (typeof event !== "string" || event.length === 0) {
throw new TypeError("event must be a non-empty string");
}
if (typeof listener !== "function") {
throw new TypeError("listener must be a function");
}
let bucket = this.#listeners.get(event);
if (!bucket) {
bucket = new Set();
this.#listeners.set(event, bucket);
}
bucket.add(listener);
return () => this.off(event, listener);
}
once(event, listener) {
const wrapper = (...args) => {
this.off(event, wrapper);
listener(...args);
};
wrapper.original = listener;
return this.on(event, wrapper);
}
off(event, listener) {
const bucket = this.#listeners.get(event);
if (!bucket) return false;
let removed = false;
for (const entry of bucket) {
if (entry === listener || entry.original === listener) {
bucket.delete(entry);
removed = true;
}
}
if (bucket.size === 0) this.#listeners.delete(event);
return removed;
}
emit(event, ...args) {
const bucket = this.#listeners.get(event);
if (!bucket) return false;
for (const listener of [...bucket]) listener(...args);
return true;
}
listenerCount(event) {
return this.#listeners.get(event)?.size ?? 0;
}
removeAllListeners(event) {
if (event === undefined) this.#listeners.clear();
else this.#listeners.delete(event);
}
}Report a problem with this exampleOpens a pre-filled issue on GitHub.