Basic Event Bus
A minimal implementation maps topic names to arrays of handler functions.
class EventBus {
constructor() {
this.topics = {};
}
emit(topic, data) {
const queue = this.topics[topic];
if (!queue) {
console.warn('No listeners for: ' + topic);
return;
}
queue.forEach(handler => handler(data));
}
on(topic, handler) {
if (!this.topics[topic]) {
this.topics[topic] = [];
}
this.topics[topic].push(handler);
}
}
Usage:
const bus = new EventBus();
bus.emit('alert', 'early message'); // warns: no listeners
bus.on('alert', msg => console.log('Received:', msg));
bus.emit('alert', 'broadcast complete');
Source-Verified Variant
To ensure subscribers only receive events from approved origins, the bus can validate the event source during disptach.
class VerifiedEventBus {
constructor() {
this.registry = {};
}
dispatch({ topic, payload, source }) {
const channel = this.registry[topic];
if (!channel) {
console.warn('Topic unregistered: ' + topic);
return;
}
if (!source) {
throw new Error('Missing source for topic: ' + topic);
}
if (source === 'any') {
channel.listeners.forEach(l => l.handler(payload));
return;
}
let matched = false;
channel.listeners.forEach(l => {
if (l.allowedSource === source) {
matched = true;
l.handler(payload);
}
});
if (!matched) {
console.warn('No listener accepts source "' + source + '" on topic "' + topic + '"');
}
}
register({ topic, source }, handler) {
if (!this.registry[topic]) {
this.registry[topic] = { listeners: [] };
}
this.registry[topic].listeners.push({
allowedSource: source,
handler
});
}
}
Usage:
const bus = new VerifiedEventBus();
bus.dispatch({ topic: 'news', payload: 'headline 1', source: 'agency' });
// warns: Topic unregistered: news
bus.register({ topic: 'news', source: 'agency' }, data => {
console.log('Agency feed:', data);
});
bus.dispatch({ topic: 'news', payload: 'headline 2', source: 'agency' });
// Agency feed: headline 2
bus.dispatch({ topic: 'news', payload: 'gossip', source: 'blog' });
// warns: No listener accepts source "blog" on topic "news"