PubSub pattern

If you ever worked with YUI or Dojo frameworks, you must be familiar with this pattern, it consists on subscribing a widget’s event by supplying a method to run when the event occurs. The PubSub pattern is not new but has great effect in a language like JavaScript which is event driven.

How does it work

Imagine that we have a widget that loads on a page, and there are a set of procedures that we want to run when that widget fully loads. A conventional way to achieve this is by calling the methods from the widget, however this doesn’t seem right, since you may be using the widget in other pages. The proper way is to implement a mechanism that informs when the widget has loaded, and then run the procedures. We can easily implement this with the PubSub pattern.

Advantages

There are two main advantages using this method: independence and manageability. The object, widget or module that fires the event doesn’t care about their subscribers structure and if they still exist or not. We can also subscribe or stop subscribing without interacting directly with the publisher owner. So the code can be isolated which facilitates its management.

Disadvantages

The main disadvantage is that we need to know exactly how the environment should work. When we have multiple subscribers and publishers it’s easy to inadvertently create an endless loop.

Implementation

First we need the PubSub constructor, it should create a stack that stores all the necessary information to run the handlers when the event fire and set the name of the topic. The name of the topic is used when the event is fired to inform the handler of the occurred event’s name.

var PubSub = function (topic) {  
    this._topic = topic || "";
    this._stack = [];
};

Next we need the subscribe method. This method should store the subscription information: the handler function, an arguments object and a flag that defines if the arguments object should be used as scope or not.

PubSub.prototype = {  
    subscribe: function (fHandler, oScope, bOverride) {
        this._stack.push({
            handler: fHandler,
            scope: (bOverride && typeof oScope === "object") ? oScope : window,
            args: oScope || {},
            override: bOverride
        });
    }
};

Now we should create the method to fire (publish) the event. This method may receive some arguments if needed and run the subscribers, stored in the stack, according to their settings. The subscription handler will receive as parameters the PubSub’s topic, the subscription arguments and the publish arguments:

PubSub.prototype = {  
    ...
    publish: function (args) {
        var i, li, cur;
        args = args || [];
        for (i = 0, li = this._stack.length; i < li; ++i) {
            cur = this._stack[i];
            cur.handler.call(cur.scope, this._topic, cur.args, args);
        }
    }
};

The publish method could be improved by using the Array.forEach if available, instead of the conventional for loop.

The last required method is the unsubscribe, that removes a subscription from the stack by the defined handler.

PubSub.prototype = {  
    ...
    unsubscribe: function (fHandler) {
        var cur, res = [];
        while (this._stack.length) {
            cur = this._stack.shift();
            if (cur.handler !== fHandler) {
                res.push(cur);
            }
        }
        this._stack = res;
    }
};

In more advanced browsers you can use the Array.filter instead of the while statement.

There are other useful methods to have in the PubSub object, but not mandatory. The unsubscribeAll, unsubscribes all methods and is very useful when destroying a widget. The getSubscribers gets all the subscribers and enables to debug or filter the handlers.

PubSub.prototype = {  
    ...
    getSubscribers: function () {
        return this._stack;
    },
    unsubscribeAll: function () {
        this._stack = [];
    }
};

Putting all together:

var PubSub = function (topic) {  
    this._topic = topic || "";
    this._stack = [];
};

PubSub.prototype = {


    subscribe: function (fHandler, oScope, bOverride) {
        this._stack.push({
            handler: fHandler,
            scope: (bOverride && typeof oScope === "object") ? oScope : window,
            args: oScope || {},
            override: bOverride
        });
    },


    publish: function (args) {
        var i, li, cur;
        args = args || [];
        for (i = 0, li = this._stack.length; i < li; ++i) {
            cur = this._stack[i];
            cur.handler.call(cur.scope, this._topic, cur.args, args);
        }
    },

    unsubscribe: function (fHandler) {
        var cur, res = [];
        while (this._stack.length) {
            cur = this._stack.shift();
            if (cur.handler !== fHandler) {
                res.push(cur);
            }
        }
        this._stack = res;
    },

    getSubscribers: function () {
        return this._stack;
    },

    unsubscribeAll: function () {
        this._stack = [];
    }
};

How to use it

The usage is fairly simple:.

var event,  
    obj1 = { a: "A", b: "B" },
    obj2 = { c: "C", d: "D" };

// create a new PubSub instance setting the topic (testing)
event = new PubSub("testing");

// create handler for the subscription
// the received arguments are the topic, the subscription arguments
// and the published arguments
function fn(sTopic, oSubArgs, oPubArgs) {  
    console.log("inside the handler", sTopic, oSubArgs, oPubArgs);
}

// subscribe the event
event.subscribe(fn, obj1, false);

// publish the event
event.publish(obj2); // logs the information from the handler

// remove the subscription
event.unsubscribe(fn);

// publish the event
event.publish(); // shouldn't log anything  

Conclusion

This a very simple pattern that should be used to keep the modularity of the scripts, leading to clean and reliable code. You may optimise the widget or add more events in an independent way without worrying about side effects.