UNPKG

als-event-emitter

Version:

A powerful, asynchronous, promise-based event emitter with support for chaining and advanced event handling.

303 lines (221 loc) 8.62 kB
# als-event-emitter `als-event-emitter` is a lightweight, powerful implementation of an event emitter system, designed to be easy to use and extendable. It supports chaining, returns promises for event result handling, and is compatible with both Node.js and browser environments, providing more flexibility compared to the standard Node.js EventEmitter. ## What's New ### Version 5.3.0 - **Event Name in Context**: Every listener now receives the event name as part of the context object (`{ name, next, resolve, reject, result }`). This is useful for identifying the event currently being processed, especially when using `onAny`. - **Group Functionality**: Added support for grouping listeners with the `group` parameter in `on`, `once`, `onLast`, and `onceLast`. Listeners assigned to a group will be executed when any event in that group is emitted. This allows for more organized listener management across related events. - **Note**: Group functionality does not apply to global listeners (`onAny`, `onceAny`). Example: ```js const emitter = new EventEmitter(); emitter.on('group1', ({ next }) => { console.log('Group listener'); next(); }); emitter.on('event1', ({ next }) => { console.log('Event 1 listener'); next(); }, { group: 'group1' }); emitter.emit('event1'); // Output: // Group listener // Event 1 listener ``` ## Installation ```bash npm install als-event-emitter ``` ## Basic Usage ```js const EventEmitter = require('als-event-emitter'); // Create an emitter const emitter = new EventEmitter(); // Register a listener emitter.on('data', (payload) => { console.log('Received data:', payload); }); // Emit an event emitter.emit('data', { message: 'Hello World' }); ``` > **Note:** In default mode (`new EventEmitter(true)`), `emit` returns a Promise and listeners receive `{ next, resolve, reject, result }`. If you pass `false` to the constructor (`new EventEmitter(true)`) `emit` is synchronous. ## Browser usage ```html <script src="/node_modules/als-event-emitter/emitter.js"></script> <script> const emitter = new EventEmitter(); </script> ``` ## API Overview ### `new EventEmitter(chain = true | false)` - Creates a new emitter instance. - If `chain = true`, `emit` becomes async and returns a `Promise`. - If `chain = false`, `emit` is synchronous. ### `emitter.on(eventName, listener, options?)` - Adds a listener for `eventName`. - If `chain = true`, `listener` receives `( { next, resolve, reject, result, name }, ...args )`. - `options` can include: - `once`: Executes the listener only once. - `last`: Ensures the listener runs at the end of the chain. - `group`: Links the listener to a group. If another event in the same group is emitted, the group listener is also triggered. ### `emitter.once(eventName, listener)` - Adds a one-time listener for `eventName`. ### `emitter.off(eventName)` - Removes all listeners for `eventName`. ### `emitter.removeListener(listener)` - Removes the given `listener` from all events and from the global `anyChain`. ### `emitter.removeAllListeners()` - Removes **all** registered listeners from **all** events (and clears `anyChain` for this instance). ### `emitter.emit(eventName, ...args)` - Emits the event. - If `chain = true`, returns a `Promise` if at least one of function in chain is async. - If `chain = false`, executes listeners synchronously and returns `undefined`. ### `emitter.onAny(listener, { once, last })` - Registers a listener that will be called for **any** event emitted by the instance. ### `emitter.onceAny(listener)` - One-time version of `onAny`. ### `emitter.has(eventName)` - Checks if there are any listeners for `eventName`. --- ## Global vs. Instance Listeners ### **Global (Static) Listeners** - `EventEmitter.on(...)`, `EventEmitter.once(...)`, etc. - These listeners are stored in a global chain (`SmartPromise.chain`) and are shared by **all** `EventEmitter` instances. - Use `EventEmitter.removeAllListeners()` to clear all static/global listeners. ### **Instance Listeners** - `emitter.on(...)`, `emitter.once(...)`, etc. - Stored in the instance's own map of events. - Call `emitter.removeAllListeners()` to clear the instance's own listeners (including `anyChain` of that instance). --- ## Advanced Usage ### `onAny` and `onceAny` ```js const emitter = new EventEmitter(); let counter = 0; // Called for any event emitter.onAny(() => counter++); emitter.emit('foo'); // counter = 1 emitter.emit('bar'); // counter = 2 ``` ### `onLast`, `onceLast` ```js emitter.onLast('myEvent', ({ next }) => { console.log('Runs last for myEvent'); next(); }); ``` These functions place the listener at the end of the chain, ensuring it runs after normal listeners. ### Grouped Listeners ```js const emitter = new EventEmitter(); emitter.on('groupA', ({ next }) => { console.log('Group A listener'); next(); }); emitter.on('eventX', ({ next }) => { console.log('Event X listener'); next(); }, { group: 'groupA' }); emitter.emit('eventX'); // Output: // Group A listener // Event X listener ``` --- ## Behavior with No Registered Event - If you call `emit('unknownEvent')` and no specific listeners exist for `'unknownEvent'`, **any global listeners** (e.g. via `onAny`, `onceAny`) **will still fire**. - This is helpful if you want to log or debug **every** event, even unregistered ones. ```js const emitter = new EventEmitter(); emitter.onAny(() => console.log('A global listener triggered!')); emitter.emit('doesNotExist'); // Logs: "A global listener triggered!" ``` --- ## Chaining and Promises 1. **If `chain = true`:** Each listener receives: - `next(result?)`: Proceed to the next listener in the chain. - `resolve(value)`: Resolve the chain immediately. - `reject(error)`: Reject the chain immediately. - `result`: The value passed from the previous listener’s `next(result)`. - `name`: The name of the current event being processed. 2. The final `emit(...)` call returns a `Promise`, which resolves or rejects based on the chain’s flow. ```js const emitter = new EventEmitter(true); emitter.on('process', ({ next }, data) => { console.log('Step 1:', data); next('step1-done'); }); emitter.on('process', ({ resolve, result }) => { console.log('Step 2:', result); resolve('All done'); }); emitter.emit('process', 'start-data') .then(finalValue => console.log('Final:', finalValue)); ``` --- ## Concurrent and Multi-Emitter Usage ```js // Multiple Emitters sharing global listeners EventEmitter.on(() => console.log('Global Listener')); const emitter1 = new EventEmitter(); const emitter2 = new EventEmitter(); emitter1.emit('someEvent'); // triggers global listener emitter2.emit('otherEvent'); // triggers global listener ``` For concurrency in async mode: ```js const emitter = new EventEmitter(true); emitter.on('test', async ({ resolve }, val) => { // Asynchronous listener await new Promise(r => setTimeout(r, 100)); resolve(`Done-${val}`); }); // Fire multiple emits concurrently Promise.all([ emitter.emit('test', 'X'), emitter.emit('test', 'Y') ]).then(([resX, resY]) => { console.log(resX, resY); // "Done-X", "Done-Y" }); ``` --- ## Backward Incompatibility Since version 5.0, `emit` can return a `Promise`, and listener signatures have changed to receive `( { next, resolve, reject, result }, ...args )` in chaining mode. If migrating from older versions, ensure your listeners are updated accordingly. --- ## Examples ### Basic Usage ```js const emitter = new EventEmitter(); emitter.on('data', (payload) => { console.log('Got data:', payload); }); emitter.emit('data', { key: 'value' }); ``` ### Using `onceAny` ```js const emitter = new EventEmitter(); let called = 0; emitter.onceAny(() => { called++; console.log('Triggered by any event once'); }); emitter.emit('eventA'); emitter.emit('eventB'); // won’t trigger again console.log('called =', called); // 1 ``` ### Using `onLast` ```js const emitter = new EventEmitter(true); emitter.on('myEvent', ({ next }) => { console.log('First Listener'); next(); }); emitter.onLast('myEvent', ({ next }) => { console.log('Last Listener'); next(); }); emitter.emit('myEvent').then(() => console.log('All done')); ```