From f403b72292dcdf40baeed8e798aa41bcf0569d43 Mon Sep 17 00:00:00 2001 From: Sergey Krasnov Date: Mon, 18 Mar 2024 08:28:25 +0300 Subject: [PATCH] feat: method added --- README.md | 2 +- src/__tests__/notifier.bench.ts | 8 +++-- src/index.ts | 60 +++++++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index e6e8bcd..79e44e2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## to install the package ```bash -npm install event-notifier +npm install @ksv90/event-notifier ``` ## after that you can use diff --git a/src/__tests__/notifier.bench.ts b/src/__tests__/notifier.bench.ts index e350ae4..5d5cf77 100644 --- a/src/__tests__/notifier.bench.ts +++ b/src/__tests__/notifier.bench.ts @@ -4,19 +4,21 @@ import { EventNotifier } from '../index'; import { Data } from './context'; describe('benchmarking', () => { - const fn = ({ value = 1 }) => value + 1; + const fn = ({ value = 1 }) => { + value + 1; + }; bench('on method without data', () => { const eventNotifier = new EventNotifier(); eventNotifier.on('test', fn); - eventNotifier.notify({ type: 'test' }); + eventNotifier.emit({ type: 'test' }); eventNotifier.off('test', fn); }); bench('on method with data', () => { const eventNotifier = new EventNotifier(); eventNotifier.on('balance', fn); - eventNotifier.notify({ type: 'balance', value: 42 }); + eventNotifier.emit({ type: 'balance', value: 42 }); eventNotifier.off('balance', fn); }); }); diff --git a/src/index.ts b/src/index.ts index 1b602fd..91d1208 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,31 +1,69 @@ -export type EventNotifierListener = (data: TData extends object ? TData : object) => void; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type EventNotifierListener = (data: TData extends object ? TData : object) => void | Promise; export type NotifyType = { type: TType }; +export type NotifyListenerOptions = { + readonly once?: boolean; +}; + export class EventNotifier> { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - protected listenerMap = new Map>>(); + protected listenerMap = new Map< + keyof TEventNotifierData, + Map + >(); + + public emit( + event: TEventNotifierData[TType] extends object ? NotifyType & TEventNotifierData[TType] : NotifyType, + ): void { + const { type, ...data } = event; + this.listenerMap.get(type)?.forEach((options, listener, map) => { + if (options?.once) map.delete(listener); + void listener(data); + }); + } public on( type: TType, listener: EventNotifierListener, + options?: NotifyListenerOptions, ): void { - const listeners = this.listenerMap.get(type) ?? new Set(); + const listeners = this.listenerMap.get(type) ?? new Map(); + listeners.set(listener, options); this.listenerMap.set(type, listeners); - listeners.add(listener); } public off( type: TType, listener: EventNotifierListener, ): void { - this.listenerMap.get(type)?.delete(listener); + this.listenerMap.get(type)?.forEach((_, currentListener, map) => { + if (currentListener !== listener) return; + map.delete(listener); + }); } - public notify( - event: TEventNotifierData[TType] extends object ? NotifyType & TEventNotifierData[TType] : NotifyType, - ): void { - const { type, ...data } = event; - this.listenerMap.get(type)?.forEach((listener) => listener(data)); + public once( + type: TType, + listener: EventNotifierListener, + options?: Omit, + ) { + this.on(type, listener, { ...options, once: true }); + } + + public has( + type: TType, + listener: EventNotifierListener, + ): boolean { + const listeners = this.listenerMap.get(type); + return !!listeners && listeners.has(listener); + } + + public delete(type: TType): void { + this.listenerMap.delete(type); + } + + public clear(): void { + this.listenerMap.clear(); } }