Spaghetti code comes from all of your objects needing to know where all your other objects are so they can communicate. I used to attempt to alleviate this problem by creating a switchboard object that acted as a message bus. That helped surprisingly little, as I had to deal with long object paths; and the bus would fill up with a ton of simplistic pass-through functions, making for tedious coding and bloating. Then I discovered dojo.event.topic, which already is the switchboard I needed, and beyond that, helps make the application act as more of an event-driven system.

The Dojo 0.9 Topic system has been greatly simplified over that found in Dojo 0.4. Actually, Topic itself is gone (now a private object), and the system has been rolled into dojo._base.connect, where it can be utilized without having to specifically require it. The two primary methods are publish and subscribe, with a third being unsubscribe.


The Dojo 0.9+ Topic (pub/sub) System

The current application I’m working on has the user’s name in several places. A crude way to design this would be: on application load, build the widget, access the userData object, find the name, and create the HTML. If the user changes his name, the userData object (or the switchboard) would have to know every place in the app that uses the name, and call those functions that would rebuild the HTML with the update. Not only is this extra work, but problems arise when function names change, arguments change, or object paths change.

The methodology for refactoring this as an event driven system: on application load, build the widget without the username HTML, using a placeholder if necessary. Then subscribe to an event of the user’s name changing:

dojo.subscribe("/app/name/update", this, "buildNameFunc");

This tells the Dojo system to listen to the event (topic) of /app/name/ update, and if it occurs, access the scope of this and call the function buildNameFunc. The topic can be any opaque string, but the convention is to break it down into: / namespace / item / event. Now in the userData object, when the user logs in, publish an event:

dojo.publish("/app/name/update", [ "Mike" ] );

The arguments are put into an array slot, but this is for Dojo code simplification. They are received as a single argument, unless you comma delineate them. When the user logs in, “Mike” is published to every object that subscribed to this event, and Charlie’s name is populated throughout.

This is great for code maintainability. Say the client has requested a feature that the user be able to change his name. Now it’s simply a matter of building the widget that handles the input with the published event. Done.

Wrapping Topics

There’s one problem with the pub/sub system. If the subscribe happens after the publish, it essentially missed the event and won’t update. Because of this, my code started turning to spaghetti again, as the objects still needed to access the initial data. I considered calling publish after a subscribe, but this would trigger all of the previous subscribes multiple times. If you have ten subscribes to a topic, the first one would get called ten times. Then I realized that the subscribe function is essentially passing in a callback. It seemed a simple matter of utilizing that callback for initialization.

I created a global object, and since Dojo wasn’t using it anymore, I named it Topic. I changed all of my subscribe/publish functions to talk to this object instead of the Dojo object. The first version of the code used the same functions, acting as pass-throughs to the Dojo functions. After getting that working properly (with a lot of existing code in the app), I added the mechanism to save the published arguments, a hash map of the unique topic strings:

this.current[topic] = args;

Next, when the subscribe function is called, it uses the passed scope and method to return argument:

scope[method].call(scope, arg);

And that’s it in a nutshell. The final code:

acme.topic = function(){
	// current topic argument hash map
	this.current = {};
};

dojo.extend(acme.topic, {
	
	subscribe: function(topic, scope, method, acceptNull){

		var hdl = dojo.subscribe(topic, scope, method)
		
		//Initializer - called only once, on subscribe
		if(acceptNull || this.current[topic]){
			// Converting arg to a non-array if it is not a length > 1
			var a = this.current[topic];
			var arg = (a == null) ?  null : (a.length == 1) ? a[0] : a;
			
			//NOTE! Only accepting string methods 
			scope[method].call(scope, arg);
		}
		return hdl;
	},
	
	publish: function(topic, args){
		dojo.publish(topic, args);
		// Store published arg, in the event a subscribe is called later
		this.current[topic] = args;
	},
	
	unsubscribe: function(handle){
		// Pass-through. Could be called directly.
		dojo.unsubscribe(handle);	
	}
	
})

The final code also fixes the argument, which as you recall, is passed in as an array. I also added an acceptNull argument, because some subscribers would want to know if the current argument is null, while a null argument could blow up others.

A couple notes to using this wrapper. It needs to be initialized very early; I actually do so before the app itself (Hence, I chose to make it a global object). You also need to aware that these subscribes will fire immediately on initialization, so there may need to be changes in the order that they initialize.

The Dojo pub/sub system is a major step toward writing event-driven, maintainable code. If you have an application that uses devices like deferred loading, or “parse on demand”, wrapping it with an initializer helps keep it that way.