NanoEmitter.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. /**
  2. * @module lib/NanoEmitter
  3. * This module contains the NanoEmitter class, which is a tiny event emitter powered by [nanoevents](https://www.npmjs.com/package/nanoevents) - [see the documentation for more info](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#nanoemitter)
  4. */
  5. import { createNanoEvents, type DefaultEvents, type Emitter, type EventsMap, type Unsubscribe } from "nanoevents";
  6. export interface NanoEmitterOptions {
  7. /** If set to true, allows emitting events through the public method emit() */
  8. publicEmit: boolean;
  9. }
  10. /** Class that can be extended or instantiated by itself to create an event emitter with helper methods and a strongly typed event map */
  11. export class NanoEmitter<TEvtMap extends EventsMap = DefaultEvents> {
  12. protected readonly events: Emitter<TEvtMap> = createNanoEvents<TEvtMap>();
  13. protected eventUnsubscribes: Unsubscribe[] = [];
  14. protected emitterOptions: NanoEmitterOptions;
  15. constructor(options: Partial<NanoEmitterOptions> = {}) {
  16. this.emitterOptions = {
  17. publicEmit: false,
  18. ...options,
  19. };
  20. }
  21. /** Subscribes to an event - returns a function that unsubscribes the event listener */
  22. public on<TKey extends keyof TEvtMap>(event: TKey | "_", cb: TEvtMap[TKey]): () => void {
  23. // eslint-disable-next-line prefer-const
  24. let unsub: Unsubscribe | undefined;
  25. const unsubProxy = (): void => {
  26. if(!unsub)
  27. return;
  28. unsub();
  29. this.eventUnsubscribes = this.eventUnsubscribes.filter(u => u !== unsub);
  30. };
  31. unsub = this.events.on(event, cb);
  32. this.eventUnsubscribes.push(unsub);
  33. return unsubProxy;
  34. }
  35. /** Subscribes to an event and calls the callback or resolves the Promise only once */
  36. public once<TKey extends keyof TEvtMap>(event: TKey | "_", cb?: TEvtMap[TKey]): Promise<Parameters<TEvtMap[TKey]>> {
  37. return new Promise((resolve) => {
  38. // eslint-disable-next-line prefer-const
  39. let unsub: Unsubscribe | undefined;
  40. const onceProxy = ((...args: Parameters<TEvtMap[TKey]>) => {
  41. unsub!();
  42. cb?.(...args);
  43. resolve(args);
  44. }) as TEvtMap[TKey];
  45. unsub = this.on(event, onceProxy);
  46. });
  47. }
  48. /** Emits an event on this instance - Needs `publicEmit` to be set to true in the constructor! */
  49. public emit<TKey extends keyof TEvtMap>(event: TKey, ...args: Parameters<TEvtMap[TKey]>): boolean {
  50. if(this.emitterOptions.publicEmit) {
  51. this.events.emit(event, ...args);
  52. return true;
  53. }
  54. return false;
  55. }
  56. /** Unsubscribes all event listeners */
  57. public unsubscribeAll(): void {
  58. for(const unsub of this.eventUnsubscribes)
  59. unsub();
  60. this.eventUnsubscribes = [];
  61. }
  62. }