evnty
Version:
Async-first, reactive event handling library for complex event flows in browser and Node.js
105 lines (103 loc) • 2.93 kB
JavaScript
import { err } from "./dispatch-result.js";
export class ListenerRegistry {
#listeners = new Map();
#snapshot = null;
[Symbol.toStringTag] = 'ListenerRegistry';
get size() {
return this.#listeners.size;
}
has(listener) {
return this.#listeners.has(listener);
}
lacks(listener) {
return !this.has(listener);
}
off(listener) {
const entry = this.#listeners.get(listener);
if (!entry) {
return false;
}
this.#listeners.delete(listener);
this.#snapshot = null;
entry.onRemove?.();
return true;
}
on(listener, onRemove) {
if (this.has(listener)) {
return false;
}
this.#listeners.set(listener, {
once: false,
onRemove
});
this.#snapshot = null;
return true;
}
once(listener, onRemove) {
if (this.has(listener)) {
return false;
}
this.#listeners.set(listener, {
once: true,
onRemove
});
this.#snapshot = null;
return true;
}
clear() {
for (const entry of this.#listeners.values()){
entry.onRemove?.();
}
this.#listeners.clear();
this.#snapshot = null;
}
dispatch(...values) {
const listeners = this.#listeners;
if (listeners.size === 0) {
return [];
}
let ordered = this.#snapshot;
if (!ordered) {
const snapshot = new Array(listeners.size);
let index = 0;
for (const fn of listeners.keys()){
snapshot[index++] = fn;
}
snapshot.length = index;
ordered = snapshot;
this.#snapshot = snapshot;
}
if (ordered.length === 0) {
return [];
}
const results = new Array(ordered.length);
const argCount = values.length;
for(let index = 0; index < ordered.length; index++){
const fn = ordered[index];
if (listeners.get(fn)?.once) {
this.off(fn);
}
const invoke = fn;
try {
switch(argCount){
case 0:
results[index] = invoke();
break;
case 1:
results[index] = invoke(values[0]);
break;
case 2:
results[index] = invoke(values[0], values[1]);
break;
default:
results[index] = invoke(...values);
break;
}
} catch (error) {
results[index] = err(error);
}
}
return results;
}
}
//# sourceMappingURL=listener-registry.js.map