// source: https://deno.land/x/eventemitter@1.2.1/mod.ts /** The callback type. */ type Callback = (...args: any[]) => any | Promise; /** A listener type. */ type Listener = Callback & { __once__?: true }; /** The name of an event. */ type EventName = string | number; type EventsType = { [key: string]: Callback } & { [key: number]: Callback }; /** * The event emitter. */ export class EventEmitter { /** * This is where the events and listeners are stored. */ private _events_: Map> = new Map(); /** * Listen for a typed event. * @param event The typed event name to listen for. * @param listener The typed listener function. */ public on(event: K, listener: E[K]): this; /** * Listen for an event. * @param event The event name to listen for. * @param listener The listener function. */ public on(event: EventName, listener: Callback): this { if (!this._events_.has(event)) this._events_.set(event, new Set()); this._events_.get(event)!.add(listener); return this; } /** * Listen for a typed event once. * @param event The typed event name to listen for. * @param listener The typed listener function. */ public once(event: K, listener: E[K]): this; /** * Listen for an event once. * @param event The event name to listen for. * @param listener The listener function. */ public once(event: EventName, listener: Callback): this { const l: Listener = listener; l.__once__ = true; return this.on(event, l as any); } /** * Remove a specific listener in the event emitter on a specific * typed event. * @param event The typed event name. * @param listener The typed event listener function. */ public off(event: K, listener: E[K]): this; /** * Remove all listeners on a specific typed event. * @param event The typed event name. */ public off(event: K): this; /** * Remove all events from the event listener. */ public off(): this; /** * Remove a specific listener on a specific event if both `event` * and `listener` is defined, or remove all listeners on a * specific event if only `event` is defined, or lastly remove * all listeners on every event if `event` is not defined. * @param event The event name. * @param listener The event listener function. */ public off(event?: EventName, listener?: Callback): this { if (!event && listener) throw new Error('Why is there a listener defined here?'); else if (!event && !listener) this._events_.clear(); else if (event && !listener) this._events_.delete(event); else if (event && listener && this._events_.has(event)) { const _ = this._events_.get(event)!; _.delete(listener); if (_.size === 0) this._events_.delete(event); } else { throw new Error('Unknown action!'); } return this; } /** * Emit a typed event without waiting for each listener to * return. * @param event The typed event name to emit. * @param args The arguments to pass to the typed listeners. */ public emitSync(event: K, ...args: Parameters): this; /** * Emit an event without waiting for each listener to return. * @param event The event name to emit. * @param args The arguments to pass to the listeners. */ public emitSync(event: EventName, ...args: Parameters): this { if (!this._events_.has(event)) return this; const _ = this._events_.get(event)!; for (let [, listener] of _.entries()) { const r = listener(...args); if (r instanceof Promise) r.catch(console.error); if (listener.__once__) { delete listener.__once__; _.delete(listener); } } if (_.size === 0) this._events_.delete(event); return this; } /** * Emit a typed event and wait for each typed listener to return. * @param event The typed event name to emit. * @param args The arguments to pass to the typed listeners. */ public async emit( event: K, ...args: Parameters ): Promise; /** * Emit an event and wait for each listener to return. * @param event The event name to emit. * @param args The arguments to pass to the listeners. */ public async emit( event: EventName, ...args: Parameters ): Promise { if (!this._events_.has(event)) return this; const _ = this._events_.get(event)!; for (let [, listener] of _.entries()) { try { await listener(...args); if (listener.__once__) { delete listener.__once__; _.delete(listener); } } catch (error) { console.error(error); } } if (_.size === 0) this._events_.delete(event); return this; } /** * The same as emitSync, but wait for each typed listener to * return before calling the next typed listener. * @param event The typed event name. * @param args The arguments to pass to the typed listeners. */ public queue(event: K, ...args: Parameters): this; /** * The same as emitSync, but wait for each listener to return * before calling the next listener. * @param event The event name. * @param args The arguments to pass to the listeners. */ public queue(event: EventName, ...args: Parameters): this { (async () => await this.emit(event, ...(args as any)))().catch( console.error, ); return this; } } export default EventEmitter;