lstd/events.ts
2025-01-07 02:58:12 +11:00

133 lines
3.3 KiB
TypeScript

// deno-lint-ignore-file no-explicit-any
import { jit } from "./jit.ts";
export class Emitter<F extends (...args: any) => void> {
#ls: { f: F; once: boolean }[] = [];
#argc = 0;
get argc() {
return this.#argc;
}
get size() {
return this.#ls.length;
}
constructor() {
this.emit = this.#emit_fast;
}
on(f: F, once: boolean) {
this.#ls.push({ f, once });
this.#argc = Math.max(this.#argc, f.length);
if (once) this.emit = this.#emit_slow;
else this.#emit_fast = this.#compile();
if (this.emit !== this.#emit_slow) this.emit = this.#emit_fast;
return this;
}
off(f: F) {
const ls = this.#ls;
let once = false;
for (let i = ls.length; i-- !== 0; ) {
if (ls[i].f === f) {
(once = ls[i].once), ls.splice(i, 1);
break;
}
}
this.#argc = ls.length ? Math.max(...ls.map(({ f }) => f.length)) : 0;
if (!once) this.#emit_fast = this.#compile();
if (this.emit !== this.#emit_slow) this.emit = this.#emit_fast;
return this;
}
// deno-lint-ignore no-unused-vars
emit(...args: Parameters<F>) {
return false;
}
listeners() {
return this.#ls.map(({ f }) => f);
}
#emit_fast = this.#compile();
#emit_slow(...args: Parameters<F>) {
const ls = this.#ls;
(this.#ls = ls.filter(({ once }) => !once)), (this.emit = this.#emit_fast);
for (let i = 0, n = ls.length; i < n; i++)
Reflect.apply(ls[i].f, undefined, args);
return ls.length !== 0;
}
#compile(): typeof this.emit {
const ls = this.#ls.filter(({ once }) => !once);
const ps = [...Array(this.#argc).keys()].map((i) => jit.raw`a${i}`);
return jit.compiled`function emit(${jit.fragment(", ", ...ps)}) {
${jit.map(" ", ls, ({ f }) => {
return jit`${f}(${jit.fragment(", ", ...ps.slice(0, f.length))});`;
})}
return ${jit.literal(ls.length !== 0)};
}`;
}
}
export type EventMap = Record<string | symbol, (...args: any) => void>;
export class TypedEmitter<T extends EventMap> {
readonly #events: { [K in keyof T]?: Emitter<T[K]> } = Object.create(null);
on<K extends keyof T>(name: K, f: T[K]) {
return (this.#events[name] ??= new Emitter()).on(f, false), this;
}
once<K extends keyof T>(name: K, f: T[K]) {
return (this.#events[name] ??= new Emitter()).on(f, true), this;
}
off<K extends keyof T>(name: K, f: T[K]) {
const events = this.#events;
if (events[name]?.off(f).size === 0) delete events[name];
return this;
}
emit<K extends keyof T>(name: K, ...args: Parameters<T[K]>) {
const event = this.#events[name];
if (event) return event.emit.apply(event, args);
else return false;
}
listeners<K extends keyof T>(name: K) {
return this.#events[name]?.listeners() ?? [];
}
static {
this.prototype.emit = function emit(
this: TypedEmitter<EventMap>,
name: string,
a: any,
b: any,
c: any,
d: any,
e: any
) {
const event = this.#events[name];
if (!event) return false;
const { argc } = event;
if (argc < 6) {
return event.emit(a, b, c, d, e);
} else {
const args = Array(argc);
for (let i = 0; i < argc; i++) args[i] = arguments[i + 1];
return event.emit.apply(undefined, args);
}
} as any;
}
}