@fimbul-works/observable
Version:
A lightweight, strongly-typed TypeScript library for reactive programming patterns, providing observable collections, values, and event handling.
125 lines (124 loc) • 4.33 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventEmitter = void 0;
const map_js_1 = require("./map.js");
const signal_js_1 = require("./signal.js");
/**
* A strongly-typed event emitter that manages event subscriptions and emissions.
* Supports both synchronous and asynchronous event handlers.
*
* @template EventMap - Defines the mapping of event names to their data types.
*/
class EventEmitter {
/**
* Internal map of event signals, where each signal corresponds to an event type.
* @private
*/
#signals = new map_js_1.ObservableMap();
/**
* Subscribes to an event with a callback function.
* The callback can be synchronous or asynchronous (return a Promise).
*
* @template K - The event key type.
* @param event - The event to subscribe to.
* @param fn - The function to be called when the event is emitted.
* @returns A cleanup function that removes the handler when called.
*/
on(event, fn) {
if (!this.#signals.has(event)) {
this.#signals.set(event, new signal_js_1.Signal());
}
const signal = this.#signals.get(event);
if (signal)
return signal.connect(fn);
return () => { };
}
/**
* Unsubscribes a callback function from an event.
* @template K - The event key type.
* @param event - The event to unsubscribe from.
* @param fn - The function to remove.
*/
off(event, fn) {
const signal = this.#signals.get(event);
if (signal)
signal.disconnect(fn);
}
/**
* Emits an event with the provided data.
* This method runs synchronously and doesn't wait for any promises returned by handlers.
*
* @template K - The event key type.
* @param event - The event to emit.
* @param data - The data to pass to event handlers.
* @returns {this} The event emitter instance for method chaining.
*/
emit(event, data) {
const signal = this.#signals.get(event);
if (signal)
signal.emit(data);
return this;
}
/**
* Emits an event with the provided data and waits for all handlers to complete,
* including any that return Promises.
*
* @template K - The event key type.
* @param event - The event to emit.
* @param data - The data to pass to event handlers.
* @returns {Promise<this>} Promise resolving to the event emitter instance for method chaining.
*/
async emitAsync(event, data) {
const signal = this.#signals.get(event);
if (signal)
await signal.emitAsync(data);
return this;
}
/**
* Registers an error handler for a specific event.
* @template K - The event key type.
* @param event - The event to handle errors for.
* @param fn - The function to be called when an error occurs.
* @returns A cleanup function that removes the error handler when called.
*/
onError(event, fn) {
if (!this.#signals.has(event)) {
this.#signals.set(event, new signal_js_1.Signal());
}
const signal = this.#signals.get(event);
if (signal)
return signal.connectError(fn);
return () => { };
}
/**
* Removes an error handler for a specific event.
* @template K - The event key type.
* @param event - The event to remove the error handler from.
* @param fn - The error handler function to remove.
* @returns {this} The event emitter instance for method chaining.
*/
offError(event, fn) {
const signal = this.#signals.get(event);
if (signal)
signal.disconnectError(fn);
return this;
}
/**
* Returns an array of all registered event names.
* @returns {Array<keyof EventMap>} An array containing all event names.
*/
getEvents() {
return Array.from(this.#signals.keys());
}
/**
* Cleans up all event subscriptions and releases resources.
* Call this method when the event emitter is no longer needed.
*/
destroy() {
for (const [, signal] of this.#signals.entries()) {
signal.destroy();
}
this.#signals.clear();
}
}
exports.EventEmitter = EventEmitter;