Event Triggering
FastEvent provides a flexible and powerful event triggering mechanism that supports various triggering methods and options.
Event Triggering Methods
The emit or emitAsync methods are used for synchronous or asynchronous event triggering.
Basic Triggering Method
The simplest way to trigger an event is using event type and data payload as parameters:
const events = new FastEvent();
// Trigger simple event
emitter.emit('user/login', { id: 1, name: 'Alice' });
await emitter.emitAsync('user/login', { id: 1, name: 'Alice' });
// Trigger event and retain event message
emitter.emit(
'user/login',
{ id: 1, name: 'Alice' }, // Event data
true, // Whether to retain
);
await emitter.emitAsync(
'user/login',
{ id: 1, name: 'Alice' }, // Event data
true, // Whether to retain
);Message Object Form
You can also trigger events using a message object, which provides more flexibility:
emitter.emit({
type: 'user/login', // Event type
payload: { id: 1, name: 'Alice' }, // Event data payload
meta: { timestamp: Date.now() }, // Optional metadata
});
await emitter.emitAsync({
type: 'user/login', // Event type
payload: { id: 1, name: 'Alice' }, // Event data payload
meta: { timestamp: Date.now() }, // Optional metadata
});Guidelines
Event Trigger Results
emit and emitAsync execute registered listener functions and return their results when triggering events.
emitter.on('event', () => 1);
emitter.on('event', () => 2);
emitter.on('event', () => 3);
const result = emitter.emit('event'); // [1, 2, 3]
const result = await emitter.emitAsync('event'); // [1, 2, 3]Call Count Limit
When triggering events, you can limit the maximum number of times a listener is called:
// Remove listener after maximum of 3 calls
emitter.on('event', handler, { count: 3 });
// Equivalent once syntax sugar
emitter.on('event', handler, { count: 1 });
// Same as
emitter.once('event', handler);Prepend Insertion
By default, when registering listeners using on/once, the listener is added to the end of the queue. Enabling prepend=true will add the listener to the beginning of the queue instead of the end, affecting the execution order of listeners.
emitter.on('event', handler1);
emitter.on('event', handler2, { prepend: true });
// Execution order: handler2 -> handler1Retained Events
Setting retain=true retains events for subsequent subscribers:
// Trigger and retain event
emitter.emit('system/status', { online: true }, true);
// Equivalent to: emitter.emit('system/status', { online: true }, { retain: true })
// Subsequent subscribers will immediately receive the retained event
emitter.on('system/status', (message) => {
console.log('Current status:', message.payload.online);
});Return Results
emit and emitAsync execute registered listener functions and return their results when triggering events.
import { FastEvent } from 'fastevent';
const emitter = new FastEvent();
emitter.on('event', async (message) => {
await delay(100);
return 1;
});
emitter.on('event', async (message) => {
await delay(300);
return 2;
});
emitter.on('event', async (message) => {
await delay(500);
return 3;
});
// Trigger async event and wait for all listeners to complete
// @noErrors
const results = await emitter.emitAsync('event');
// ^^^^^^^
// results === [1, 2, 3]
// @noErrors
const results = emitter.emit('event');
// ^^^^^^^
// results === [1, 2, 3]Error Handling
When async listener functions throw errors, the emit/emitAsync return results will include Error objects.
const emitter = new FastEvent();
emitter.on('event', async (message) => {
await delay(100);
return 1;
});
emitter.on('event', async (message) => {
throw new Error('custom error');
});
emitter.on('event', async (message) => {
await delay(500);
return 3;
});
// Trigger async event and wait for all listeners to complete
const results = await emitter.emitAsync('event');
// results === [1,
// Error('custom error'),
// 3]Triggering with Metadata
Additional metadata can be specified when triggering events to pass to listeners.
import { FastEvent } from 'fastevent';
const emitter = new FastEvent();
emitter.emit('order/create', { orderId: '123', total: 99.99 }, false, {
timestamp: Date.now(),
source: 'web',
userId: 'user_123',
});
emitter.on('order/create', (message) => {
message.meta; // { timestamp: number; source: string; userId: string; }
});message.metais an object containing additional metadata.- Types of metadata specified during
emitcannot be automatically inferred. - For more information about metadata, see Metadata.
Type-Safe Event Triggering
When using TypeScript, FastEvent provides complete type checking:
import { FastEvent } from 'fastevent';
interface MyEvents {
'user/login': { id: number; name: string };
'user/logout': { id: number };
'system/error': { code: string; message: string };
}
const emitter = new FastEvent<MyEvents>();
emitter.onAny<number>((message) => {});
// ✅ Correct: Data type matches
emitter.emit('user/login', { id: 1, name: 'Alice' });
// ✅ Correct: Message object
emitter.emit({
type: 'user/login',
payload: { id: 1, name: 'Alice' },
});
// ✅ Correct: Supports triggering undefined event types
emitter.emit({
type: 'xxxxx',
payload: { id: 1, name: 'Alice' },
});
// ✅ Correct: Supports triggering undefined event types
emitter.emit('xxxx', 1);
// ❌ Error: Declared event type payload doesn't match
events.emit('user/login', { id: '1', name: 'Alice' }); // TypeScript error
events.emit('user/login', 1); // TypeScript error
events.emit('order', '1');
// ❌ Error: id type doesn't match
events.emit({
type: 'user/login',
payload: { id: '1', name: 'Alice' },
});The first generic parameter of FastEvent is used to declare event types:
interface MyEvents {
[EventTypeName]: <EventMessagePayloadType>
}
const events = new FastEvent<MyEvents>();Note
In the example above, we declared the MyEvents interface, why is the following type correct?
emitter.emit({
type: 'xxxxx',
payload: { id: 1, name: 'Alice' },
});Because FastEvent.emit provides multiple different function call signatures, when type is string, payload is inferred as any, so the emit function call is correct.
API
emit
emit supports multiple calling methods, with the following function signatures:
emit<R = any, T extends Types = Types>(type: T, payload?: Events[T], retain?: boolean): R[]
emit<R = any, T extends string = string>(type: T, payload?: T extends Types ? Events[Types] : any, retain?: boolean): R[]
emit<R = any, T extends string = string>(message: FastEventEmitMessage<{ [K in T]: K extends Types ? Events[K] : any }, Meta>, retain?: boolean): R[]
emit<R = any>(message: FastEventEmitMessage<Events, Meta>, retain?: boolean): R[]
//----
emit<R = any, T extends Types = Types>(type: T, payload?: Events[T], options?: FastEventListenerArgs<Meta>): R[]
emit<R = any, T extends string = string>(type: T, payload?: T extends Types ? Events[Types] : any, options?: FastEventListenerArgs<Meta>): R[]
emit<R = any, T extends string = string>(message: FastEventEmitMessage<{ [K in T]: K extends Types ? Events[K] : any }, Meta>, options?: FastEventListenerArgs<Meta>): R[]
emit<R = any>(message: FastEventEmitMessage<Events, Meta>, options?: FastEventListenerArgs<Meta>): R[]The rich call signatures of emit above can be broadly categorized into the following situations:
- Trigger events by specifying
typeandpayload
emitter.emit('click', 100);
emitter.emit('click', 100, true); // Retain message
emitter.emit('click', 100, { retain: true }); // Retain message
emitter.emit('click', 100, { ... }); // Carry additional trigger parameters- Trigger events by specifying
message
emitter.emit({ type: 'click', payload: 100 });
// Retain message
emitter.emit({ type: 'click', payload: 100 }, true);
// Retain message
emitter.emit({ type: 'click', payload: 100 }, { retain: true });
// Carry additional trigger parameters
emitter.emit({ type: 'click', payload: 100 }, { retain: true, .... });emitAsync
emitAsync is the asynchronous version of emit, implemented as follows:
public async emitAsync<R = any>(): Promise<[R | Error][]> {
const results = await Promise.allSettled(this.emit.apply(this, arguments as any))
return results.map((result) => {
if (result.status === 'fulfilled') {
return result.value
} else {
return result.reason
}
})
}It is used in the same way as emit, with the difference being that it internally calls Promise.allSettled and returns Promise<[R | Error][]>.
Trigger Parameters
The emit/emitAsync methods allow passing additional parameters that control event triggering behavior.
export type FastEventListenerArgs<M = Record<string, any>> = {
retain?: boolean;
meta?: Record<string, any> & Partial<M>;
abortSignal?: AbortSignal; // Used to pass to listener functions
executor?: FastListenerExecutorArgs;
};| Parameter Name | Type | Description |
|---|---|---|
retain | boolean | Whether to retain the message. If true, the last message is retained. See Retained Messages |
meta | Record<string, any> | Event metadata, passed to listeners |
abortSignal | AbortSignal | Used to pass to listener functions. See Aborting Listeners |
executor | FastListenerExecutorArgs | Used to control listener execution behavior. See Executor |