Executors
The essence of event triggering is calling the emit method, which calls all registered listener functions and returns the execution results of all listeners.
Under normal circumstances, the emit method calls all listener functions in the order they were registered, and then returns the execution results of all listeners.
FastEvent provides a flexible executor mechanism that allows developers to configure how multiple listeners are executed, how execution results are handled, and how performance is optimized.
Built-in Executors
FastEvent comes with the following built-in executors:
| Executor | Description |
|---|---|
parallel | Default, concurrent execution |
race | Parallel executor, using Promise.race for parallel execution |
balance | Load balancing executor |
first | Executes the first listener |
last | Executes the last listener |
random | Randomly selects a listener |
series | Serial executor, executes listeners in sequence and returns the last result |
waterfall | Executes listeners in sequence, returns the last result, stops on error |
(listeners,message,args,execute)=>any[] | Custom executor |
Using Executors
- Specify executor when triggering an event
Specify an executor when calling the emit function to trigger an event, which is only effective for the current event.
import { race } from "fastevent/executors"
const emitter = new FastEvent();
emitter.emit("event",payload,{
executor: race()
})- Specify executor globally
Specify an executor when creating the event emitter, which is effective for all events.
import { race } from "fastevent/executors"
const emitter = new FastEvent({
executor: race()
});
emitter.emit("event",payload)Custom Executors
In addition to the built-in executors, FastEvent also supports custom executors.
You can create custom executors to implement specific execution logic.
const customExecutor = (listeners, message, args, execute) => {
// listeners: array of listeners, each element is a tuple of [listener, maxCount, executedCount]
// message: event message
// args: additional parameters
// execute: function to execute a single listener
// Example: only execute the first listener
return [execute(listeners[0][0], message, args)];
};
const emitter = new FastEvent({
executor: customExecutor,
});Execution Count Management
Each listener in FastEvent is stored as a tuple: [listener, maxCount, executedCount]
listener: The listener functionmaxCount: Maximum execution count limit (0 means unlimited)executedCount: Number of times already executed
Important note:
By default, the execution count of listeners is automatically managed, and you don't need to modify it manually.
After each listener execution, FastEvent automatically decreases the executedCount of all listeners.
However, in some executors, not all listeners need to be executed, such as the race and balance executors, which only select one listener function to execute from the listener array.
This can cause the execution count of listeners to be inconsistent with expectations.
Therefore, the executor needs to correct this issue to ensure the correctness of the executedCount value.
Taking the random executor as an example, it randomly selects one listener from the list to execute.
The pseudocode for executing listeners is as follows:
class FastEvent {
_executeListeners(listeners, message, args, execute) {
try {
executor(listeners, message, args, execute);
} finally {
// The execution count of all listeners will be incremented by 1
// listener = [listener,maxCount,executedCount]
listeners.forEach((listener) => {
listener[2]++;
});
}
}
}The random executor only selects one listener from the list to execute, so it needs to manually correct the execution count of the listeners.
export const random = (listeners, message, args, execute) => {
const index = Math.floor(Math.random() * listeners.length);
// Decrement the execution count of all listeners by 1 to offset the subsequent increment
listeners.forEach((listener) => listener[2]--);
// Only increment the execution count of the selected listener by 1
listeners[index][2]++;
return [execute(listeners[index][0], message, args)];
};Why Use the Execute Function
When creating a custom listener executor, you need to use the execute function to execute the listener function.
Taking the random executor as an example, the execute function is used to execute the listener function.
export const random = (listeners, message, args, execute) => {
const index = Math.floor(Math.random() * listeners.length);
listeners.forEach((listener) => listener[2]--);
listeners[index][2]++;
// ❌ Directly executing the listener function: cannot ensure the accuracy of the listener function's context (this) and error handling
return [listeners[index][0](message, args)];
// ✅ Use execute to execute the listener function
return [execute(listeners[index][0], message, args)];
};Note
You can refer to the code implementation of built-in executors to write your own executor, see here