newListeners
The newListeners
function provides a simple utility to manage event listeners, allowing you to register listeners and notify them asynchronously while handling errors gracefully.
Implementation
function newListeners<T extends unknown[], E = unknown>(
onError: (error: E) => void = console.error,
): [
addListener: (listener: (...args: T) => void | Promise<void>) => () => void,
notifyListeners: (...args: T) => Promise<void>,
] {
const listeners: {
[listenerId: number]: (...args: T) => void | Promise<void>;
} = {};
let listenerId = 0;
function addListener(
listener: (...args: T) => void | Promise<void>,
): () => void {
const id = listenerId++;
listeners[id] = listener;
return () => {
delete listeners[id];
};
}
async function notify(...args: T): Promise<void> {
for (const listener of Object.values(listeners)) {
try {
await listener(...args);
} catch (e) {
onError(e as E);
}
}
}
return [addListener, notify];
}
Examples
const [addListener, notifyListeners] = newListeners<[string]>();
const removeListener = addListener((message) => {
console.log(`Received message: ${message}`);
});
notifyListeners("Hello, World!");
// Output: Received message: Hello, World!
removeListener();
notifyListeners("This won't be logged.");
Advanced Example with Error Handling:
const [addListener, notifyListeners] = newListeners<[string], Error>((error) => {
console.error("Listener failed:", error.message);
});
addListener(async (message) => {
console.log(`Processing: ${message}`);
if (message === "error") {
throw new Error("Intentional error.");
}
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log(`Processed: ${message}`);
});
await notifyListeners("test");
// Output:
// Processing: test
// Processed: test
await notifyListeners("error");
// Output:
// Processing: error
// Listener failed: Intentional error.
Signature
Parameters:
onError
:(error: E) => void
(optional):- A callback function to handle errors thrown by listeners.
- Defaults to
console.error
if not provided.
Returns a tuple containing two functions:
addListener
:(listener: (...args: T) => void | Promise<void>) => () => void
: Registers a new listener and returns a function to remove the listener.notifyListeners
:(...args: T) => Promise<void>
: Asynchronously invokes all registered listeners with the provided arguments.
Behavior Details
- Listener Registration:
- Each call to
addListener
registers a listener, which can be synchronous or asynchronous. - Returns a removal function to unregister the listener.
- Each call to
- Event Notification:
notifyListeners
executes all registered listeners in the order they were added.- Supports asynchronous execution and ensures listeners complete before resolving.
- Error Handling:
- Errors thrown by individual listeners are caught and passed to the
onError
handler. - Notification continues for remaining listeners even if one fails.
- Errors thrown by individual listeners are caught and passed to the
- Memory Management:
- Listeners are stored in a map and can be removed using the function returned by
addListener
. - Removing a listener prevents it from receiving further notifications.
- Listeners are stored in a map and can be removed using the function returned by
Use Cases
This function is useful for applications that involve asynchronous events or require custom error handling for event listeners:
- Implementing custom event dispatchers.
- Managing callbacks for asynchronous workflows.
- Adding and removing listeners dynamically in a modular system.
- etc
See Also
- EventEmitter in Node.js for a more feature-rich implementation of event handling.