@hapi/podium v5.0.2
npm install @hapi/podium@5.0.2yarn add @hapi/podium@5.0.2pnpm add @hapi/podium@5.0.2Event emitter with async features.
Compatibility
| Major version | License | Node.js |
|---|---|---|
| 5 | BSD | >= 16 |
Introduction
podium is an event emitter with support for tags, filters, channels, event update cloning, arguments spreading, and other features useful when building large scale applications. While node's native EventEmitter is strictly focused on maximum performance, it lacks many features that do not belong in the core implementation. podium is not restricted by node's performance requirement as it is designed for application layer needs where its overhead is largely insignificant as implementing these features will have similar cost on top of the native emitter.
Example
const Podium = require('@hapi/podium');
const emitter = new Podium.Podium();
const context = { count: 0 };
emitter.registerEvent({
name: 'event',
channels: ['ch1', 'ch2'],
});
const handler1 = function () {
++this.count;
console.log(this.count);
};
const handler2 = function () {
this.count = this.count + 2;
console.log(this.count);
};
emitter.on(
{
name: 'event',
channels: ['ch1'],
},
handler1,
context,
);
emitter.on(
{
name: 'event',
channels: ['ch2'],
},
handler2,
context,
);
emitter.emit({
name: 'event',
channel: 'ch1',
});
emitter.emit({
name: 'event',
channel: 'ch2',
});
emitter.hasListeners('event'); // true
emitter.removeAllListeners('event'); // Removes all listeners subscribed to 'event'The above example uses podium's channel event parameter to restrict the event update to only the specified channel.
First you register the event by calling the registerEvent() method. Here, you name the event 'event' and give it channels ['ch1', 'ch2'].
Next you specify your listener handlers. These will be called when an event is updated. Here you make use of podium's listener context, data that you can bind to your listener handlers. In this case, handler1 will add 1 to count, which is specified as { count: 0 }, while handler2 will add 2.
Next you call the on() method to subscribe a handler to an event. You use the same event name, but two different channels. 'ch1' will use handler1 and 'ch2' will use handler2.
Lastly, you use emit() to emit and event update to the subscribers.
Using different parameters
Along with channels, podium allows you to specify other event parameters. Below are more examples:
channels
const Podium = require('@hapi/podium');
const podiumObject = new Podium.Podium();
podiumObject.registerEvent([
{
name: 'event1',
channels: ['ch1', 'ch2', 'ch3', 'ch4'],
},
{
name: 'event2',
channels: ['ch1', 'ch2'],
},
]);
const listener1 = (data) => {
console.log('listener1 called', data);
};
const listener2 = (data) => {
console.log('listener2 called', data);
};
podiumObject.on(
{
name: 'event1',
channels: ['ch1'],
},
listener1,
);
podiumObject.on(
{
name: 'event1',
channels: ['ch3', 'ch4'],
},
listener2,
);
podiumObject.on({ name: 'event1', channels: 'ch2' }, (data) => {
// autonomous function
console.log('auto', data);
});
var arr = [0, 1, 2, 3, 4, 4, 5];
podiumObject.emit({
name: 'event1',
channel: 'ch3',
});clone
const Podium = require('@hapi/podium');
const podiumObject = new Podium.Podium();
podiumObject.registerEvent([
{
name: 'event1',
channels: ['ch1', 'ch2'],
clone: true,
},
{
name: 'event2',
channels: ['ch1', 'ch2'],
},
]);
const listener1 = (data) => {
data[0] = 55;
console.log('listener1 called', data);
};
const listener2 = (data) => {
data[0] = 100;
console.log('listener2 called', data);
};
podiumObject.on(
{
name: 'event1',
channels: ['ch1'],
},
listener1,
);
podiumObject.on(
{
name: 'event2',
channels: ['ch1'],
},
listener2,
);
var arr = [0, 1, 2, 3, 4, 4, 5];
console.log('initially: ', arr);
podiumObject.emit({
name: 'event1',
channel: 'ch1',
});
console.log('after event1, ch1: ', arr);
podiumObject.emit({
name: 'event2',
channel: 'ch1',
});
console.log('after event2, ch1: ', arr);spread
const Podium = require('@hapi/podium');
const podiumObject = new Podium.Podium();
podiumObject.registerEvent([
{
name: 'event1',
channels: ['ch1', 'ch2'],
spread: true,
},
{
name: 'event2',
channels: ['ch1', 'ch2'],
},
]);
const listener1 = (data1, data2, data3, data4) => {
console.log('listener1 called', data1, data2, data3, data4);
};
const listener2 = (data) => {
data[0] = 100;
console.log('listener2 called', data);
};
podiumObject.on(
{
name: 'event1',
channels: ['ch1'],
},
listener1,
);
podiumObject.on(
{
name: 'event2',
channels: ['ch1'],
},
listener2,
);
var arr = [0, 1, 2, 3, 4, 4, 5];
console.log('initially: ', arr);
podiumObject.emit({
name: 'event1',
channel: 'ch1',
});
console.log('after event1, ch1: ', arr);
podiumObject.emit({
name: 'event2',
channel: 'ch1',
});
console.log('after event2, ch1: ', arr);shared
const Podium = require('@hapi/podium');
const podiumObject = new Podium.Podium();
podiumObject.registerEvent([
{
name: 'event1',
channels: ['ch1', 'ch2'],
},
]);
podiumObject.registerEvent([
{
name: 'event1',
channels: ['ch1', 'ch2'],
shared: true,
},
]);
const listener2 = (data) => {
console.log('listener2 called', data);
};
podiumObject.on(
{
name: 'event1',
channels: ['ch1'],
},
listener2,
);
var arr = [0, 1, 2, 3, 4, 4, 5];
podiumObject.emit({
name: 'event1',
channel: 'ch1',
});tag-filter
const Podium = require('@hapi/podium');
const emitter = new Podium.Podium('test');
const updates = [];
emitter.on('test', (data) => updates.push({ id: 1, data }));
emitter.on({ name: 'test', filter: ['a', 'b'] }, (data) =>
updates.push({ id: 2, data }),
);
emitter.on({ name: 'test', filter: 'b' }, (data) =>
updates.push({ id: 3, data }),
);
emitter.on({ name: 'test', filter: ['c'] }, (data) =>
updates.push({ id: 4, data }),
);
emitter.on({ name: 'test', filter: { tags: ['a', 'b'], all: true } }, (data) =>
updates.push({ id: 5, data }),
);
emitter.emit({ name: 'test', tags: 'a' }, 1);
emitter.emit({ name: 'test', tags: ['b'] }, 2);
emitter.emit({ name: 'test', tags: ['d'] }, 3);
emitter.emit({ name: 'test', tags: ['a'] }, 4);
emitter.emit({ name: 'test', tags: ['a', 'b'] }, 5);
emitter.emit('test', 6, () => {
console.log(updates);
});count
const Podium = require('@hapi/podium');
const podiumObject = new Podium();
podiumObject.registerEvent('event1');
const listener1 = function (data) {
console.log('listener1 called', data);
};
podiumObject.on(
{
name: 'event1',
count: 2,
},
listener1,
);
podiumObject.emit('event1', 'emit 1');
podiumObject.emit('event1', 'emit 2');
podiumObject.emit('event1', 'emit 3'); // this wont call listener1new Podium(events, options)
Creates a new podium emitter where:
events- if present, the value is passed topodium.registerEvent().options- optional configuration options passed topodium.registerEvent().
Returns a Podium object.
podium.registerEvent(events, options)
Register the specified events and their optional configuration. Events must be registered before they can be emitted or subscribed to. This is done to detect event name misspelling and invalid event activities. The events argument can be:
- an event string.
- an event options object with the following optional keys (unless noted otherwise):
name- the event name string (required).channels- a string or array of strings specifying the event channels available. Defaults to no channel restrictions (event updates can specify a channel or not).clone- iftrue, thedataobject passed topodium.emit()is cloned before it is passed to the listeners (unless an override specified by each listener). Defaults tofalse(datais passed as-is).spread- iftrue, thedataobject passed topodium.emit()must be an array and thelistenermethod is called with each array element passed as a separate argument (unless an override specified by each listener). This should only be used when the emitted data structure is known and predictable.Defaults tofalse(datais emitted as a single argument regardless of its type).tags- iftrueand thecriteriaobject passed topodium.emit()includestags, the tags are mapped to an object (where each tag string is the key and the value istrue) which is appended to the arguments list at the end. A configuration override can be set by each listener. Defaults tofalse.shared- iftrue, the same eventnamecan be registered multiple times where the second registration is ignored. Note that if the registration config is changed between registrations, only the first configuration is used. Defaults tofalse(a duplicate registration will throw an error). For detailed examples of event parameters see here
- an array containing any of the above.
The options argument is an object with the following optional properties:
validate- iffalse, events are not validated. This is only allowed when theeventsvalue is returned fromPodium.validate(). Defaults totrue
podium.emit(criteria, data)
Emits an event update to all the subscribed listeners where:
criteria- the event update criteria which must be one of:- the event name string.
- an object with the following optional keys (unless noted otherwise):
name- the event name string (required).channel- the channel name string.tags- a tag string or array of tag strings.
data- the value emitted to the subscribers.
await podium.gauge(criteria, data)
Behaves identically to podium.emit(), but also returns an array of the results of all the event listeners that run. The return value is that of Promise.allSettled(), where each item in the resulting array is { status: 'fulfilled', value } in the case of a successful handler, or { status: 'rejected', reason } in the case of a handler that throws.
Please note that system errors such as a TypeError are not handled specially, and it's recommended to scrutinize any rejections using something like bounce.
podium.on(criteria, listener, context)
Subscribe a handler to an event where:
criteria- the subscription criteria which must be one of the following:- event name string.
- a criteria object with the following optional keys (unless noted otherwise):
name- the event name string (required).channels- a string or array of strings specifying the event channels to subscribe to. If the event registration specified a list of allowed channels, thechannelsarray must match the allowed channels. Ifchannelsare specified, event updates without any channel designation will not be included in the subscription. Defaults to no channels filter.clone- iftrue, thedataobject passed topodium.emit()is cloned before it is passed to thelistenermethod. Defaults to the event registration option (which defaults tofalse).count- a positive integer indicating the number of times thelistenercan be called after which the subscription is automatically removed. A count of1is the same as callingpodium.once(). Defaults to no limit.filter- the event tags (if present) to subscribe to which can be one of the following:- a tag string.
- an array of tag strings.
- an object with the following:
tags- a tag string or array of tag strings.all- iftrue, alltagsmust be present for the event update to match the subscription. Defaults tofalse(at least one matching tag).
spread- iftrue, and thedataobject passed topodium.emit()is an array, thelistenermethod is called with each array element passed as a separate argument. This should only be used when the emitted data structure is known and predictable. Defaults to the event registration option (which defaults tofalse).tags- iftrueand thecriteriaobject passed topodium.emit()includestags, the tags are mapped to an object (where each tag string is the key and the value istrue) which is appended to the arguments list at the end. Defaults to the event registration option (which defaults tofalse).
listener- the handler method set to receive event updates. The function signature depends on thespread, andtagsoptions.context- an object that binds to the listener handler.
podium.addListener(criteria, listener, context)
Same as podium.on().
podium.once(criteria, listener, context)
Same as calling podium.on() with the count option set to 1.
await podium.once(criteria)
Subscribes to an event by returning a promise that resolves when the event is emitted. criteria can be specified in any format supported by podium.on(), except for the count option that is set to 1.
Return a promise that resolves when the event is emitted. The resolution value is an array of emitted arguments.
podium.few(criteria)
Subscribes to an event by returning a promise that resolves when the event is emitted count times. criteria can only be specified in the object format supported by podium.on() and the count option is required.
Returns a promise that resolves when the event is emitted count times. The resolution value is an array where each item is an array of emitted arguments.
podium.off(name, listener)
Removes all listeners subscribed to a given event name matching the provided listener method where:
name- the event name string.listener- the function reference provided when subscribed.
podium.removeListener(name, listener)
Same as podium.off().
Returns a reference to the current emitter.
podium.removeAllListeners(name)
Removes all listeners subscribed to a given event name where:
name- the event name string.
Returns a reference to the current emitter.
podium.hasListeners(name)
Returns whether an event has any listeners subscribed where:
name- the event name string.
Returns true if the event name has any listeners, otherwise false.
Podium.validate(events)
Validates that events are declared in the correct format. Events can be declared in any of the formats supported by the podium.registerEvent() method. When the declaration is valid, the array of events returned can be passed to the podium.registerEvent() method with validations disabled, otherwise a validation error is thrown.