Published: Wed 16 May 2012
By Jason
In JavaScript .
When building web applications with sophisticated client-side behavior (i.e., you have a lot of JavaScript) I work hard to define loosely-coupled JavaScript modules. Intentional developers strive for structured and modular server-side code, but because of JavaScript's history we tend not to take the same care with it. We then repeat the sins of the past when we find ourselves with hundreds or thousands of lines of JavaScript that aren't encapsulated and decoupled.
Key Point: Write loosely-coupled JavaScript modules within your application.
Of course, you do need some coupling between modules. To realize the interactions that must occur between JavaScript modules, consider using a global event technique. Modules publish information to the world when something interesting happens; other modules subscribe to information they need to react to.
Key Point: Publishers shouldn't need to know anything about their subscribers.
Here are a few ways to set up a global publish/subscribe mechanism your JavaScript modules to use:
Roll Your Own
We don't want to succumb to the "Not Invented Here" problem, but these few lines create a simple but useful window.Bus object that supports global events.
( function () {
var subscriptions = null ;
window . Bus = {
reset : function () {
subscriptions = {};
},
subscribe : function ( messageType , callback ) {
if ( typeof subscriptions [ messageType ] === 'undefined' ) {
subscriptions [ messageType ] = [];
}
subscriptions [ messageType ]. push ( callback );
},
publish : function ( messageType , args ) {
if ( typeof subscriptions [ messageType ] === 'undefined' ) return ;
var subscribers = subscriptions [ messageType ];
for ( var i = 0 ; i < subscribers . length ; i ++ ) {
subscribers [ i ]( args );
}
}
};
window . Bus . reset ();
}());
// Subscriber usage:
window . Bus . subscribe ( 'UserCreated' , function ( user ) {
alert ( 'User ' + user . Name + ' created!' );
});
// Publisher usage:
var newUser = { Name : "The Hulk" };
window . Bus . publish ( 'UserCreated' , newUser );
jQuery
One quick way to get there without adding any more dependencies than you probably already have is it just use features jQuery's eventing system. Here's code that demonstrates how to use jQuery as a global pub/sub framework. In this example, there are two modules, a publisher and a subscriber, each with a bit of HTML and a bit of JavaScript. The JavaScript comments explain the main points.
< html >
< head >
< title > Client-side Pub/Sub with jQuery</ title >
<!-- add reference to jQuery -->
</ head >
< body >
< div >
< input id = 'PublishButton' type = 'button' value = 'Publish' />
</ div >
< b > Events:</ b >
< div id = 'Subscriber1' >
</ div >
</ body >
< script type = 'text/javascript' >
// This function block correlates with the publisher's scope.
$ ( function () {
// Represents a secret value only the publisher knows about directly,
// but will include in its event args.
var someNumber = 7 ;
// When the button is clicked we publish an event.
$ ( "#PublishButton" ). click ( function () {
var dt = new Date ();
//
// The next line is the important one; notice how the event args
// are passed as an array.
//
$ . event . trigger ( 'CustomEventName' , [ someNumber , dt ]);
someNumber ++ ;
});
});
// This function block correlates with the subscriber's scope.
$ ( function () {
//
// Notice how we're not calling bind() on the subscriber like a typical
// jQuery event, but calling it on our own UI component.
// Also notice how the event args, which were published as an array,
// are flattened into individual parameters.
//
$ ( "#Subscriber1" ). bind ( 'CustomEventName' , function ( event , number , date ) {
$ ( "<div>" ). text ( "Event published: " + number + " @ " + date ). appendTo ( $ ( this ));
});
});
</ script >
</ html >
Use a Global Event Library
The following JavaScript libraries are a few that provide a global pub/sub infrastructure:
There are comments .