EventManager.ts 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. interface EventListener<T = unknown> {
  2. callback: (payload: T) => void;
  3. target?: unknown;
  4. once: boolean;
  5. }
  6. export class EventManager<TEvents extends object = Record<string, unknown>> {
  7. private readonly listeners = new Map<keyof TEvents, Set<EventListener>>();
  8. on<K extends keyof TEvents>(event: K, callback: (payload: TEvents[K]) => void, target?: unknown): void {
  9. this.add(event, callback, target, false);
  10. }
  11. once<K extends keyof TEvents>(event: K, callback: (payload: TEvents[K]) => void, target?: unknown): void {
  12. this.add(event, callback, target, true);
  13. }
  14. off<K extends keyof TEvents>(event: K, callback: (payload: TEvents[K]) => void, target?: unknown): void {
  15. const bucket = this.listeners.get(event);
  16. if (!bucket) return;
  17. for (const listener of bucket) {
  18. if (listener.callback === callback && listener.target === target) {
  19. bucket.delete(listener);
  20. }
  21. }
  22. if (bucket.size === 0) this.listeners.delete(event);
  23. }
  24. offTarget(target: unknown): void {
  25. for (const [event, bucket] of this.listeners) {
  26. for (const listener of bucket) {
  27. if (listener.target === target) bucket.delete(listener);
  28. }
  29. if (bucket.size === 0) this.listeners.delete(event);
  30. }
  31. }
  32. emit<K extends keyof TEvents>(event: K, payload?: TEvents[K]): void {
  33. const bucket = this.listeners.get(event);
  34. if (!bucket) return;
  35. for (const listener of [...bucket]) {
  36. try {
  37. (listener.callback as (value: TEvents[K]) => void).call(listener.target, payload);
  38. } catch (error) {
  39. console.error(`[EventManager] listener failed: ${String(event)}`, error);
  40. }
  41. if (listener.once) bucket.delete(listener);
  42. }
  43. if (bucket.size === 0) this.listeners.delete(event);
  44. }
  45. clear(): void {
  46. this.listeners.clear();
  47. }
  48. private add<K extends keyof TEvents>(event: K, callback: (payload: TEvents[K]) => void, target: unknown, once: boolean): void {
  49. let bucket = this.listeners.get(event);
  50. if (!bucket) {
  51. bucket = new Set<EventListener>();
  52. this.listeners.set(event, bucket);
  53. }
  54. for (const listener of bucket) {
  55. if (listener.callback === callback && listener.target === target) return;
  56. }
  57. bucket.add({ callback: callback as (payload: unknown) => void, target, once });
  58. }
  59. }