diff --git a/src/index.test.ts b/src/index.test.ts index 055d9e1..dd97d7d 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -101,6 +101,23 @@ test.concurrent('test sync event listenerCount', () => { expect(eventEmitter.listenerCount('ev1')).toBe(0) }) +test('offAll should remove listener options for specific event', () => { + jest.useFakeTimers() + const eventEmitter = SyncEvent.new() + const handler = jest.fn() + + eventEmitter.on('ev1', handler, { debounce: { waitMs: 50 } }) + eventEmitter.offAll('ev1') + + eventEmitter.on('ev1', handler) + eventEmitter.emit('ev1', '', 1) + eventEmitter.emit('ev1', '', 1) + jest.advanceTimersByTime(100) + + expect(handler).toHaveBeenCalledTimes(2) + jest.useRealTimers() +}) + test.concurrent('test sync event on and once', () => { const eventEmitter = SyncEvent.new() let count = 0 diff --git a/src/sync-event.ts b/src/sync-event.ts index 9e480c7..5f6101c 100644 --- a/src/sync-event.ts +++ b/src/sync-event.ts @@ -212,13 +212,25 @@ export class SyncEvent implements ISyncEvent { */ public offAll = (event?: K): this => { if (event) { - // FIXME memory leak - this.#listenerCount -= this.#handlerMap.delete(event)?.size ?? 0 - this.#handlerMap.set(event, new CollectionSet()) + const handlers = this.#handlerMap.delete(event) + if (handlers) { + this.#listenerCount -= handlers.size + + handlers.forEach(handler => { + this.#listenerConfigMap.delete(handler) + }) + + this.#onceHandlerWrapperMap.forEach((wrapper, origin) => { + if (wrapper.type === event) { + this.#onceHandlerWrapperMap.delete(origin) + } + }) + } } else { this.#handlerMap.clear() this.#onceHandlerWrapperMap.clear() this.#anyHandlerSet.clear() + this.#listenerConfigMap.clear() this.#listenerCount = 0 } @@ -239,6 +251,7 @@ export class SyncEvent implements ISyncEvent { const successDeleted = handlers.delete(handler) if (successDeleted) { this.#listenerCount -= 1 + this.#listenerConfigMap.delete(handler) } }