Skip to content

EventEmitterX — API Reference

Overview

EventEmitterX is a cross-platform EventEmitter implementation compatible with both Node.js and browsers. It extends the standard Node.js EventEmitter API with additional features for debugging, performance monitoring, and advanced asynchronous patterns.


Import

typescript
import { EventEmitterX } from '@termi/eventemitterx/modules/events';
// or
import EventEmitterX, { once, on } from '@termi/eventemitterx/modules/events';

Constructor

typescript
new EventEmitterX(options?: ConstructorOptions)

ConstructorOptions

OptionTypeDefaultDescription
maxListenersnumberInfinityMaximum number of listeners per event before a warning is emitted
listenerOncePerEventTypebooleanfalseIf true, a listener can be registered at most once per event type (similar to EventTarget behavior)
captureRejectionsbooleanfalseEnable automatic rejection capture for async listeners
emitCounterConsole | ICounterCount emit calls. Pass console for console.count or a custom ICounter
listenerWithoutThisbooleanfalseCall listener functions without binding this to the emitter
supportEventListenerObjectbooleanfalseSupport DOM { handleEvent } pattern
isDebugTraceListenersbooleanfalseAttach call stack to listeners via listener.__debugTrace

Example

typescript
const emitter = new EventEmitterX({
  maxListeners: 50,
  listenerOncePerEventType: true,
  emitCounter: console,
});

Instance Methods

Standard EventEmitter Methods

These methods behave identically to Node.js EventEmitter:

MethodSignature
on(event, listener)Add a listener for event. Returns this.
addListener(event, listener)Alias for on.
once(event, listener)Add a one-time listener. Removed after first call. Returns this.
off(event, listener)Alias for removeListener.
removeListener(event, listener)Remove a specific listener. Returns this.
removeAllListeners(event?)Remove all listeners (optionally for a specific event). Returns this.
prependListener(event, listener)Add listener to the beginning of the list. Returns this.
prependOnceListener(event, listener)Add one-time listener to the beginning. Returns this.
emit(event, ...args)Emit an event. Returns true if listeners existed.
listeners(event)Returns a copy of the listener array (unwraps once-wrappers).
rawListeners(event)Returns a copy of the raw listener array (includes once-wrappers).
listenerCount(event)Returns the number of listeners for event.
eventNames()Returns an array of event names with registered listeners.
setMaxListeners(n)Set max listeners. Returns this.
getMaxListeners()Get current max listeners value.

Non-Standard Methods

hasListeners(event)

Check if there are any listeners for the given event.

typescript
if (emitter.hasListeners('data')) {
  emitter.emit('data', payload);
}

hasListener(event, listenerToCheck?)

Check if a specific listener is registered. If listenerToCheck is omitted, behaves like hasListeners.

typescript
const handler = () => console.log('event!');
emitter.on('data', handler);
emitter.hasListener('data', handler); // true

destructor()

Destroy the emitter. Emits kDestroyingEvent, removes all listeners, and prevents future listener additions.

typescript
emitter.destructor();
emitter.isDestroyed; // true

[Symbol.dispose]()

Alias for destructor(). Enables using syntax (TC39 Explicit Resource Management).

typescript
{
  using emitter = new EventEmitterX();
  emitter.on('data', handler);
} // automatically destroyed here

Static Methods

EventEmitterX.once() — Enhanced Promise-based Once

This is the most significant departure from Node.js events.once(). It supports:

  • Multiple event names — resolves on whichever fires first
  • Filter function — only resolve when the filter returns true
  • Timeout — reject with TimeoutError after specified ms
  • AbortSignal — reject with AbortError when signal is aborted
  • AbortControllers array — group multiple abort controllers
  • ServerTiming — track timing metrics
  • Custom error event name — override the default 'error' event
  • onDone callback — called before promise resolves
  • Custom Promise constructor — use a custom Promise implementation
  • Works with EventEmitter, EventTarget, and compatible emitters

Signatures

typescript
// With EventEmitterX
static once(emitter: EventEmitterX, types: EventName | EventName[], options?: StaticOnceOptions): Promise<any[]>;

// With Node.js EventEmitter
static once(emitter: NodeEventEmitter, types: string | symbol | (string | symbol)[], options?: StaticOnceOptions): Promise<any[]>;

// With DOM EventTarget
static once(eventTarget: EventTarget, types: string | string[], options?: StaticOnceOptionsEventTarget): Promise<[Event]>;

Options (StaticOnceOptions)

OptionTypeDescription
prependbooleanAdd listener at the beginning of the list
signalAbortSignalCancel the waiting with an abort signal
abortControllersAbortController[]Group of abort controllers to listen to
timeoutnumberTimeout in ms before rejection with TimeoutError
errorEventNameEventNameCustom error event name (default: 'error')
filter(emitEventName, ...args) => booleanFilter function — only resolve when returns true
onDone(emitEventName, ...args) => voidCallback executed before resolve
timingServerTimingServer-Timing tracking
PromisePromiseConstructorCustom Promise constructor
debugInfoObjectDebug info attached to timeout/abort errors

Examples

Basic usage:

typescript
const emitter = new EventEmitterX();

setTimeout(() => emitter.emit('ready', { status: 'ok' }), 1000);

const [result] = await EventEmitterX.once(emitter, 'ready');
console.log(result); // { status: 'ok' }

Multiple event names (race):

typescript
const emitter = new EventEmitterX();

// Resolves on whichever fires first
const result = await EventEmitterX.once(emitter, ['success', 'failure']);

With filter:

typescript
const emitter = new EventEmitterX();

// Only resolve when the value is greater than 10
const [value] = await EventEmitterX.once(emitter, 'data', {
  filter: (eventName, value) => value > 10,
});

With timeout:

typescript
try {
  const result = await EventEmitterX.once(emitter, 'response', {
    timeout: 5000,
  });
} catch (error) {
  // error.name === 'TimeoutError'
  // error.code === 'ETIMEDOUT'
}

With AbortSignal:

typescript
const controller = new AbortController();

setTimeout(() => controller.abort(), 3000);

try {
  await EventEmitterX.once(emitter, 'data', {
    signal: controller.signal,
  });
} catch (error) {
  // error.name === 'AbortError'
}

With DOM EventTarget:

typescript
const button = document.querySelector('#myButton');

const [event] = await EventEmitterX.once(button, 'click', {
  timeout: 10000,
  passive: true,
});

Edge case — waiting for 'error' itself:

typescript
// When waiting for 'error' event itself, no special error handling is applied
const [error] = await EventEmitterX.once(emitter, 'error');

EventEmitterX.on() — Async Iterator

Creates an async iterator for event streams. Alias for eventsAsyncIterator.

typescript
static on(emitter: EventEmitter | EventTarget, event: EventName, options?: Options): AsyncIterator

Options

OptionTypeDescription
signalAbortSignalAbort the iterator
computeValue(eventName, eventArgs, addCallback) => TTransform event args before yielding
stopEventNameEventNameEvent that stops the iterator
errorEventNameEventNameEvent that causes the iterator to throw (default: 'error')

Example

typescript
const emitter = new EventEmitterX();

// Produce events
setInterval(() => emitter.emit('tick', Date.now()), 1000);

// Consume as async iterator
for await (const [timestamp] of EventEmitterX.on(emitter, 'tick')) {
  console.log('Tick at:', timestamp);
  if (timestamp > deadline) break;
}

EventEmitterX.getEventListeners(emitter, eventName)

Get listeners from any compatible emitter (EventEmitterX, Node EventEmitter, or EventTarget).

typescript
const listeners = EventEmitterX.getEventListeners(emitter, 'data');

EventEmitterX.addAbortListener(signal, callback)

Safely listen to an AbortSignal abort event. Returns a Disposable.

typescript
const disposable = EventEmitterX.addAbortListener(signal, () => {
  console.log('Aborted!');
});

// Later: cleanup
disposable[Symbol.dispose]();

Static Properties

PropertyTypeDescription
errorMonitorsymbolSymbol for monitoring error events without catching them
captureRejectionSymbolsymbolSymbol.for('nodejs.rejection')
usingDomainsfalseDomains are not supported

Special Events

EventWhen emitted
'newListener'Before a new listener is added
'removeListener'After a listener is removed
'error'On error. If no listener — throws.
'duplicatedListener'When listenerOncePerEventType is enabled and a duplicate listener is detected
kDestroyingEventDuring destructor() call, before all listeners are removed

TypeScript Generics

typescript
interface MyEvents {
  data: (payload: Buffer) => void;
  error: (err: Error) => void;
  close: () => void;
}

const emitter = new EventEmitterX<MyEvents>();

emitter.on('data', (payload) => {
  // payload is typed as Buffer
});

const [payload] = await EventEmitterX.once(emitter, 'data');

Compatibility Notes

  • EventEmitterEx is a deprecated alias for EventEmitterX
  • EventEmitter is also exported as an alias
  • ICompatibleEmitter / IMinimumCompatibleEmitter interfaces define the minimum contract for static once() compatibility

Released under the ISC License.