Publisher is a JavaScript Publish/Subscribe (Pub/Sub) library crafted to handle event-driven communication. Provides pub/sub functionality with extensive wildcard support, async/sync publishing, priority and invocation options, content based filtering & more.
The heart of Publisher lies in its uniquely optimized hierarchical data structure, providing fast subscriber matching even with extensive subscription sets. Unlike traditional flat Pub/Sub systems, Publisher allows you to easily organize events into structured topics and leverage wildcard subscriptions for additional flexibility.
Whether you're building scalable web applications or complex front-end architectures, Publisher ensures your events and notifications are handled gracefully and reliably, delivering ease of use combined with powerful features.
- Topic Hierarchy & Wildcards: Manage event complexity with a structured hierarchy and wildcard topic matching.
- Persisted Messages: Ensure subscribers never miss critical events, delivering persistent messages immediately upon subscribing.
- Priority & Invocations: Gain fine-grained control over execution order and limit subscription triggers, improving predictability and efficiency.
- Async & Exception Handling: Dispatch events asynchronously or synchronously with built-in exception handling.
- Conditional Execution: Execute subscriptions only when specific conditions are met.
- Global Configuration: Configure default behavior globally for asynchronous dispatch, error handling, and unsubscribing behavior.
npm install @alphanull/publisher
Download latest version from jsDelivr Download latest version from unpkg
Download release from GitHub
publisher can be used as ES6 module (recommended) but also via require
in NodeJS or with direct access to a global variable:
import { publish, subscribe, unsubscribe } from '@alphanull/publisher';
const { publish, subscribe, unsubscribe } = require('@alphanull/publisher');
<script src="path/to/publisher.min.cjs"></script>
const { publish, subscribe, unsubscribe } = publisher;
Quickly set up a simple Pub/Sub interaction:
import { publish, subscribe, unsubscribe } from '@alphanull/publisher';
const handler = data => {
console.log(`User logged in: ${data.username}`);
}
// Receiver: subscribe to a specific topic
const token = subscribe('login', handler);
// Sender: publish an event
publish('login', { username: 'Alice' });
// Receiver: unsubscribe using the token (recommended)
unsubscribe(token);
// Receiver: alternatively, unsubscribe using topic and handler
unsubscribe('login', handler);
By utilizing topic hierarchies and wildcards, you can subscribe to multiple events. A hierachy is created by using the /
delimiter to create topic segments, while a *
is used to match any topic segment:
// Subscribe to ALL topics
subscribe('*', (data, topic) => {
console.log(`Event ${topic} received:`, data);
});
// Subscribe to all "user"-related topics, INCLUDING "user"
subscribe('user', (data, topic) => {
console.log(`Event ${topic} received:`, data);
});
// Subscribe to all "user"-related topics, EXCLUDING "user"
subscribe('user/*', (data, topic) => {
console.log(`Event ${topic} received:`, data);
});
// Matching multiple topics with wildcards
subscribe('app/*/update', (data, topic) => {
console.log(`Update from ${topic}:`, data);
});
publish('user/logout', { username: 'Bob' }); // triggers first, second & third subscriber
publish('app/profile/update', { username: 'Charlie' }); // triggers first and fourth subscribers
publish('app/settings/update', { theme: 'dark' }); // triggers first and fourth subscribers
You can also use an array of tokens to quickly unsubscribe multiple handlers. In addition, adding true
to the second argument (or the third, in case you use topic/handler for unsubscribe) does not fail with an error if the matching token was not found.
const tokens = [
subscribe('topic/1', handler),
subscribe('topic/2', handler)
];
// Batch unsubscribe
unsubscribe(tokens);
// Lenient unsubscribe, silently ignores non-existing tokens
unsubscribe(9999, true);
By default, all events are sent asynchronously. You can override this behavior globally (see 10.) or with individual publish
actions by using async: false
as an option. In addition, when using synchronous publish
, any subscriber is able to cancel an event, so that subsequent subscribers are not notified anymore. So basically, this works similar to the cancellation of DOM Events.
// return false in a handler cancels the chain
subscribe('sync/event', () => false);
// Synchronous event publishing can be canceled
publish('sync/event', {}, { async: false, cancelable: true });
Usually, subscribers are notifed in the order they subscribed, i.e. the first subscriber is receiving the first message. You can change this behavior adding a priority
option, where higher numbers are executed first, with 0
being the default.
// Control subscriber order with priorities
subscribe('priority/event', () => console.log('second'), { priority: 1 });
subscribe('priority/event', () => console.log('first'), { priority: 2 });
publish('priority/event');
It is also possible to limit the number of handler invocations by adding the invocations
option, this being a positive number counting down when the handler is called. Once the counter reaches 0
the handler is automatically unsubscribed. For example, the following code executes the handler only on the first publish
occurence:
// Limit subscription invocations
subscribe('limited/event', () => console.log('I only execute once'), { invocations: 1 });
publish('limited/event'); // triggers handler
publish('limited/event'); // not triggered anymore, handler was unsubscribed
Run subscriptions based on conditional logic, so that the handler is only invoked if the function specified by the condition
option returns true:
subscribe('data/event', data => {
console.log('Condition met:', data);
}, {
condition: data => data.status === 'success'
});
publish('data/event', { status: 'success' }); // triggers subscriber
publish('data/event', { status: 'error' }); // ignored
Ensure certain messages are received even when the subscription is done after the actual message was already sent. For this to happen, both publish
and subscribe
have to use the persist: true
option. It is also possible to remove a perssitent message later on using removePersistentMessage
.
import { publish, subscribe, removePersistentMessage } from './publisher.js';
// make message persistent
publish('app/ready', { status: 'ready' }, { persist: true });
// Subscribers immediately receive persistent messages upon subscription
subscribe('app/ready', data => console.log('Persistently received:', data), { persist: true });
// after removing, later subscriber don't receive the event anymore
removePersistentMessage('app/ready');
By default, if a handler throws an Error, it is caught by the publisher so that subsequent subscribers are still being executed. Instead the error is output to the console (if possible). This behavior can be changed globally, or per publish
so that exceptions are not caught anymore.
subscribe('error/event', () => {
throw new Error('Subscriber error!');
});
subscribe('error/event', () => {
console.log('I still might be executed');
});
// Errors caught internally, other subscribers remain unaffected
publish('error/event', data , { handleExceptions: true });
// Throws an error, publishing is halted
publish('error/event', data , { handleExceptions: false });
Configure Publisher.js globally to tailor its behavior. All subsequent actions will use the newly set option(s), unless locally overidden.
import { configure } from './publisher.js';
// Equivalent to the default configuration
configure({
async: true, // Global async dispatch
handleExceptions: true, // Global error handling
lenientUnsubscribe: true // No errors on unsubscribing non-existent subscribers
});
For more detailed docs, see JSDoc Documentation
Copyright © 2015–present Frank Kudermann @ alphanull.de