UNPKG

evnty

Version:

Async-first, reactive event handling library for complex event flows in browser and Node.js

135 lines (133 loc) 3.38 kB
import { Disposer } from "./async.js"; import { Signal } from "./signal.js"; import { ListenerRegistry } from "./listener-registry.js"; import { DispatchResult } from "./dispatch-result.js"; export class EventIterator { #signal; constructor(signal){ this.#signal = signal; } async next() { try { const value = await this.#signal.receive(); return { value, done: false }; } catch { return { value: undefined, done: true }; } } async return() { return { value: undefined, done: true }; } } export class Event { #listeners = new ListenerRegistry(); #signal = new Signal(); #disposer; #onDispose; #sink; [Symbol.toStringTag] = 'Event'; constructor(onDispose){ this.#onDispose = onDispose; this.#disposer = new Disposer(this); } get sink() { return this.#sink ??= this.emit.bind(this); } handleEvent(event) { this.emit(event); } get size() { return this.#listeners.size; } emit(value) { this.#signal.emit(value); return new DispatchResult(this.#listeners.dispatch(value)); } lacks(listener) { return !this.#listeners.has(listener); } has(listener) { return this.#listeners.has(listener); } off(listener) { this.#listeners.off(listener); return this; } on(listener) { this.#listeners.on(listener); return ()=>void this.off(listener); } once(listener) { this.#listeners.once(listener); return ()=>void this.off(listener); } clear() { this.#listeners.clear(); return this; } receive() { return this.#signal.receive(); } then(onfulfilled, onrejected) { return this.receive().then(onfulfilled, onrejected); } catch(onrejected) { return this.receive().catch(onrejected); } finally(onfinally) { return this.receive().finally(onfinally); } settle() { return this.receive().then((value)=>({ status: 'fulfilled', value }), (reason)=>({ status: 'rejected', reason })); } [Symbol.asyncIterator]() { return new EventIterator(this.#signal); } dispose() { this[Symbol.dispose](); } [Symbol.dispose]() { if (this.#disposer[Symbol.dispose]()) { this.#signal[Symbol.dispose](); this.#listeners.clear(); void this.#onDispose?.(); } } } export const merge = (...events)=>{ const mergedEvent = new Event(()=>{ for (const event of events){ event.off(mergedEvent.sink); } }); for (const event of events){ event.on(mergedEvent.sink); } return mergedEvent; }; export const createInterval = (interval)=>{ let counter = 0; const intervalEvent = new Event(()=>clearInterval(timerId)); const timerId = setInterval(()=>{ intervalEvent.emit(counter++); }, interval); return intervalEvent; }; export const createEvent = ()=>new Event(); export default createEvent; //# sourceMappingURL=event.js.map