diff --git a/__tests__/eventEmitter.ts b/__tests__/eventEmitter.ts index d0227d1..d56e01c 100644 --- a/__tests__/eventEmitter.ts +++ b/__tests__/eventEmitter.ts @@ -99,6 +99,33 @@ describe("eventEmitter", () => { expect(logUserLogin2).toHaveBeenCalled(); }); + it("handles unsubscription of one listener by another during emission", () => { + const logUserLogin1 = jest.fn(); + const logUserLogin2 = jest.fn(); + + // eslint-disable-next-line prefer-const + let unsubscribeSecond!: ReturnType; + + subscribe("userLogin", (eventName, data) => { + logUserLogin1(eventName, data); + unsubscribeSecond(); + }); + + unsubscribeSecond = subscribe("userLogin", logUserLogin2); + + // First emission: both listeners should be called once + emit("userLogin", { userId: "user1", timestamp: new Date() }); + + expect(logUserLogin1).toHaveBeenCalledTimes(1); + expect(logUserLogin2).toHaveBeenCalledTimes(1); + + // Second emission: only the first listener should fire + emit("userLogin", { userId: "user1", timestamp: new Date() }); + + expect(logUserLogin1).toHaveBeenCalledTimes(2); + expect(logUserLogin2).toHaveBeenCalledTimes(1); + }); + it("should not throw if emit is called without any listeners", () => { expect(() => { emit("userLogin", { userId: "user1", timestamp: new Date() }); diff --git a/src/eventEmitter.ts b/src/eventEmitter.ts index 93696be..eaac88e 100644 --- a/src/eventEmitter.ts +++ b/src/eventEmitter.ts @@ -478,12 +478,13 @@ export const createEventEmitter = < if (!listenersMapEntry) { return; } - listenersMapEntry.listeners.forEach(({ listener, predicate }) => { + const listeners = [...listenersMapEntry.listeners.values()]; + for (const { listener, predicate } of listeners) { if (predicate && !predicate(data)) { - return; + continue; } listener(eventName, data); - }); + } }; // Unsubscribe all listeners for a specific event or all events