event-bus.js en JavaScript
Un bus de eventos casi tipado: suscribir, emitir, cancelar y esperar un evento.
#!/usr/bin/env node
class EventBus {
#handlers = new Map();
on(type, handler) {
if (!this.#handlers.has(type)) this.#handlers.set(type, new Set());
this.#handlers.get(type).add(handler);
return () => this.off(type, handler);
}
off(type, handler) {
const handlers = this.#handlers.get(type);
if (!handlers) return false;
const removed = handlers.delete(handler);
if (handlers.size === 0) this.#handlers.delete(type);
return removed;
}
once(type, { signal } = {}) {
const { promise, resolve, reject } = Promise.withResolvers();
const stop = this.on(type, (payload) => {
stop();
resolve(payload);
});
signal?.addEventListener("abort", () => {
stop();
reject(signal.reason ?? new Error("aborted"));
}, { once: true });
return promise;
}
emit(type, payload) {
const handlers = this.#handlers.get(type);
if (!handlers) return 0;
let delivered = 0;
for (const handler of [...handlers]) {
try {
handler(payload);
delivered += 1;
} catch (error) {
this.emit("error", new Error(`handler for ${type} threw`,
{ cause: error }));
}
}
return delivered;
}
get types() {
return [...this.#handlers.keys()];
}
}
const bus = new EventBus();
const seen = [];
const stopRuns = bus.on("run", (payload) => seen.push(payload.tpm));
bus.on("run", () => { throw new Error("bad handler"); });
bus.on("error", (error) => seen.push(`error: ${error.cause.message}`));
console.log("delivered:", bus.emit("run", { tpm: 104 }));
console.log("types:", bus.types.sort());
stopRuns();
console.log("after unsubscribe:", bus.emit("run", { tpm: 98 }));
setTimeout(() => bus.emit("cleared", { set: "Async" }), 5);
const cleared = await bus.once("cleared");
console.log("awaited:", cleared.set);
const controller = new AbortController();
setTimeout(() => controller.abort(new Error("gave up")), 5);
const failed = await bus.once("never", { signal: controller.signal })
.catch((error) => error.message);
console.log(failed, seen);
Cómo funciona
- Los manejadores viven en un Map de Sets, así quitar es barato.
onceresuelve una promesa, que se compone con await.- Un manejador que lanza no debe frenar a los demás.
Palabras clave y builtins usados aquí
MapPromiseSetawaitcatchclassconstforifletofreturnthisthrowtry
El intento, en números
- Líneas
- 77
- Caracteres a escribir
- 1902
- Tokens
- 586
- Ritmo de tres estrellas
- 110 tpm
Al ritmo de tres estrellas de 110 tokens por minuto, este intento toma unos 320 segundos.
Paso 1 de 1 en Bis; paso 13 de 13 en El navegador.