Application-level publish/subscribe messaging system.
Funky.PubSub provides decoupled communication between components without requiring direct references. Unlike Funky.Events which works with DOM elements, PubSub is for application-level messaging.
PubSub topics use colon notation with funky: prefix:
// ✅ Correct - colon notation
'funky:trade:created'
'funky:user:loaded'
'funky:cache:cleared'
// ❌ Wrong - dot notation (use for DOM events with E)
'funky.trade.created'// Subscribe to a topic
Funky.PubSub.on('funky:user:loaded', function(user) {
console.log('User loaded:', user.name);
});
// Publish to a topic
Funky.PubSub.emit('funky:user:loaded', { id: 1, name: 'John' });
// One-time subscription
Funky.PubSub.once('funky:app:ready', function() {
console.log('App initialized!');
});| Method | Description | Returns |
|---|---|---|
PubSub.on(topic, callback) |
Subscribe to topic | Function (unsubscribe) |
PubSub.off(topic, callback?) |
Unsubscribe from topic | undefined |
PubSub.once(topic, callback) |
One-time subscription | Function (unsubscribe) |
PubSub.emit(topic, data?) |
Publish to topic | undefined |
| Method | Description | Returns |
|---|---|---|
PubSub.has(topic) |
Check if topic has subscribers | boolean |
PubSub.hasListeners(topic) |
Alias for has() |
boolean |
PubSub.subscribers(topic) |
Get subscriber count | number |
PubSub.listenerCount(topic) |
Alias for subscribers() |
number |
PubSub.eventNames() |
Get all registered topic names | string[] |
PubSub.clear(topic?) |
Remove subscribers | PubSub (chainable) |
| Method | Description | Returns |
|---|---|---|
PubSub.namespace(ns) |
Create namespaced channel | Object |
// Subscribe
Funky.PubSub.on('funky:trade:created', function(trade) {
console.log('New trade:', trade.symbol);
});
// Publish
Funky.PubSub.emit('funky:trade:created', {
id: 123,
symbol: 'AAPL',
quantity: 100
});// Auto-removes after first fire
Funky.PubSub.once('funky:user:loaded', function(user) {
initializeUserUI(user);
});
// Useful for initialization
Funky.PubSub.once('funky:app:ready', function() {
startApplication();
});// Store handler reference for removal
var handler = function(data) {
console.log('Trade updated:', data);
};
Funky.PubSub.on('funky:trade:updated', handler);
// Later: remove specific handler
Funky.PubSub.off('funky:trade:updated', handler);
// Or remove all handlers for topic
Funky.PubSub.off('funky:trade:updated');// Check if anyone is listening
if (Funky.PubSub.has('funky:cache:cleared')) {
console.log('Something is listening for cache clear');
}
// Get subscriber count
var count = Funky.PubSub.subscribers('funky:trade:created');
console.log('Trade subscribers:', count);// Clear specific topic
Funky.PubSub.clear('funky:trade:created');
// Clear ALL subscribers (use with caution!)
Funky.PubSub.clear();// Create module-scoped pub/sub
var tradeEvents = Funky.PubSub.namespace('funky:trade');
// Subscribe (actually subscribes to 'funky:trade:created')
tradeEvents.on('created', function(trade) {
console.log('Trade created:', trade);
});
tradeEvents.on('updated', function(trade) {
console.log('Trade updated:', trade);
});
tradeEvents.on('deleted', function(id) {
console.log('Trade deleted:', id);
});
// Emit within namespace
tradeEvents.emit('created', { id: 123, symbol: 'AAPL' });
// Check subscribers
if (tradeEvents.has('created')) {
console.log('Has trade created listeners');
}
// Clear all trade events
tradeEvents.clear();When you call Funky.PubSub.namespace(ns), you get an object with these methods:
| Method | Description |
|---|---|
.on(topic, callback) |
Subscribe to namespaced topic |
.once(topic, callback) |
One-time namespaced subscription |
.off(topic, callback?) |
Unsubscribe from namespaced topic |
.emit(topic, data?) |
Publish to namespaced topic |
.has(topic) |
Check for namespaced subscribers |
.subscribers(topic) |
Count namespaced subscribers |
.clear() |
Clear all topics in namespace |
All topics are automatically prefixed with {namespace}: - e.g., funky:trade:created.
// In TradeModule
Funky.PubSub.emit('funky:trade:created', tradeData);
// In NotificationModule (no direct reference needed)
Funky.PubSub.on('funky:trade:created', function(trade) {
showNotification('Trade created: ' + trade.symbol);
});
// In AuditModule
Funky.PubSub.on('funky:trade:created', function(trade) {
logAuditEvent('trade_created', trade);
});// Store handlers for cleanup
var handlers = {
onTradeCreated: function(trade) { /* ... */ },
onTradeUpdated: function(trade) { /* ... */ }
};
// Subscribe
Funky.PubSub.on('funky:trade:created', handlers.onTradeCreated);
Funky.PubSub.on('funky:trade:updated', handlers.onTradeUpdated);
// Cleanup
function destroy() {
Funky.PubSub.off('funky:trade:created', handlers.onTradeCreated);
Funky.PubSub.off('funky:trade:updated', handlers.onTradeUpdated);
}// Only emit if someone is listening
if (Funky.PubSub.has('funky:expensive:operation')) {
var result = performExpensiveOperation();
Funky.PubSub.emit('funky:expensive:operation', result);
}| Aspect | Funky.Events (E) |
Funky.PubSub |
|---|---|---|
| Scope | DOM element | Application |
| Notation | Dots (.) |
Colons (:) |
| Prefix | funky. |
funky: |
| Element Required | Yes | No |
| Use Case | UI events | App messaging |
// DOM Events (E) - requires element
E.on(element, 'funky.modal.opened', handler);
// PubSub - no element
Funky.PubSub.on('funky:user:loaded', handler);-
Use consistent topic naming:
// Pattern: funky:{module}:{action} 'funky:trade:created' 'funky:user:logout' 'funky:cache:invalidated'
-
Store handlers for removal:
var handler = function() { /* ... */ }; Funky.PubSub.on('topic', handler); // Later: Funky.PubSub.off('topic', handler);
-
Use namespaces for modules:
var events = Funky.PubSub.namespace('funky:mymodule'); events.on('action', handler);
-
Use
once()for one-time events:Funky.PubSub.once('funky:app:initialized', startApp);
-
Check before heavy operations:
if (Funky.PubSub.has('funky:data:ready')) { Funky.PubSub.emit('funky:data:ready', expensiveData); }
- Funky.Events - DOM event handling
- Event Conventions - Naming patterns
- ServiceWorker - Uses PubSub for SW events