JavaScript: Observer Pattern / Publish-Subscribe

In this post, I will describe how to create a reusable implementation of the Observer pattern (also known as Publish-Subscribe or Dependents (1)). This implementation can be applied to any JavaScript object to instantly make it observable! The implementation can also be used to create a global topic registry.

First, what is the Observer Pattern?

The Observer Pattern, as described by the ‘Gang of Four’, defines a one to many dependency between objects, so that when one object changes state, all of its dependents are notified and updated automatically (1).

Next, let’s implement a reusable version of the Observer in JavaScript

[code language=”JavaScript”]
function publisher(that) {
var subscribers = [];
that = that || {};

/**
* Attach a topic handler
* @param topic
* @param handler
*/
that.on = function(topic, handler) {
if (typeof subscribers[type] === ‘undefined’) {
subscribers[topic] = [];
}
subscribers[topic].push(handler);
};

/**
* Notify observers by calling their topic handlers
* @param topic
* @param params
*/
that.fire = function(topic, params) {
var handlers = subscribers[topic] || [];
for (var i=0; i < handlers.length; i++) {
handlers[i](params);
}
};

/**
* Detach a topic handler
* @param topic
* @param handler
*/
that.remove = function(topic, handler) {
var handlers = subscribers[topic];
for (var i=0; i < handlers.length; i++) {
if (subscribers[i] === handler) {
subscribers.splice(i, 1);
}
}
};

return that;
}
[/code]

Finally, let’s turn an ordinary JavaScript object into a publisher.

[code language=”JavaScript”]
// Create a subject
var subject = {
name : ‘Carmine’
};

// Create an observer – will register with the subject
var observer = {
handleEvent : function(e) {
console.log(‘observer handled event with data ‘+ e.key);
}
};

// Update the ordinary subject object to be a publisher!
publisher(subject);

// Register the observer with the eventTopic topic
subject.on(‘eventTopic’, observer.handleEvent);

// Fire the event
subject.fire(‘eventTopic’, { key : ‘event data’});
[/code]

This publisher is not required to be imparted on secondary objects. This particular publisher implementation can very well stand on its own. Simply use the publisher to register topics via publisher.on('topic, handler) and fire events via publisher.fire(...). This usage paradigm essentially provides to you a global publisher. On the other hand, when imparting a publisher on specific objects (like in the sample code above), you reduce the topic space to that of the object.

That’s it! Happy publishing 🙂

References

(1) Design Patterns Elements of Reusable Object Oriented Software

You may also like...