diff --git a/source/event-emitter.ts b/source/event-emitter.ts index 9c6b7ed..96c200a 100644 --- a/source/event-emitter.ts +++ b/source/event-emitter.ts @@ -158,7 +158,11 @@ export class EventEmitter { // Emit the event to all listeners. listeners.forEach((entry) => { - entry.wrappedListener(readonlyPayload); + try { + entry.wrappedListener(readonlyPayload); + } catch (error) { + console.error(error); + } }); // Return true if there are listeners for the event, false otherwise. diff --git a/test/event-emitter.test.ts b/test/event-emitter.test.ts index dd32b33..865a68e 100644 --- a/test/event-emitter.test.ts +++ b/test/event-emitter.test.ts @@ -89,6 +89,24 @@ const testEventEmitterEmitReturnsTrueWithListeners = (): void => { expect(hasListeners).toBe(true); }; +/** + * Tests that EventEmitter.emit continues after a listener throws an error. + */ +const testEventEmitterEmitContinuesAfterListenerThrows = (): void => { + const emitter = new EventEmitter(); + const secondListener = vi.fn(); + + emitter.on('message', (): void => { + throw new Error('listener failure'); + }); + emitter.on('message', secondListener); + + emitter.emit('message', 'hello'); + + expect(secondListener).toHaveBeenCalledOnce(); + expect(secondListener).toHaveBeenCalledWith('hello'); +}; + /** * Tests that emitted events cannot be mutated. */ @@ -666,6 +684,7 @@ const runTests = async (): Promise => { test('EventEmitter: only calls listeners for the emitted event type', testEventEmitterCallsOnlyMatchingListeners); test('EventEmitter: returns false when emitting with no listeners', testEventEmitterEmitReturnsFalseWithNoListeners); test('EventEmitter: returns true when emitting with listeners', testEventEmitterEmitReturnsTrueWithListeners); + test('EventEmitter: continues after a listener throws an error', testEventEmitterEmitContinuesAfterListenerThrows); test('EventEmitter: emitted events cannot be mutated', testEventEmitterEmittedEventsCannotBeMutated); test('EventEmitter: stops calling a listener after its off callback is invoked', testEventEmitterOffCallbackRemovesListener); test('EventEmitter: removes a listener when off is called with the same reference', testEventEmitterOffRemovesListenerByReference);