#Four Ways To Do Pub/Sub With jQuery and jQuery UI (in the future)
Between jQuery 1.7 and some of work going into future versions of jQuery UI, there are a ton of hot new ways for you to get your publish/subscribe on. Here are just four of them, three of which are new.
(PS: If you're unfamiliar with pub/sub, read the guide to it that Julian Aubourg and I wrote here http://msdn.microsoft.com/en-us/scriptjunkie/hh201955.aspx)
##Option 1: Using jQuery 1.7's $.Callbacks() feature:
var topics = {}; jQuery.Topic = function( id ) { var callbacks, topic = id && topics[ id ]; if ( !topic ) { callbacks = jQuery.Callbacks(); topic = { publish: callbacks.fire, subscribe: callbacks.add, unsubscribe: callbacks.remove }; if ( id ) { topics[ id ] = topic; } } return topic; };
Usage:
// Subscribers $.Topic( 'mailArrived' ).subscribe( fn1 ); $.Topic( 'mailArrived' ).subscribe( fn2 ); $.Topic( 'mailSent' ).subscribe( fn1 ); // Publisher $.Topic( 'mailArrived' ).publish( 'hello world!' ); $.Topic( 'mailSent' ).publish( 'woo! mail!' ); // Here, 'hello world!' gets pushed to fn1 and fn2 // when the 'mailArrived' notification is published // with 'woo! mail!' also being pushed to fn1 when // the 'mailSent' notification is published. /* output: hello world! fn2 says: hello world! woo! mail! */
##Option 2: Custom events using .on() and .off():
In jQuery 1.7, we updated the events API to support two new methods: .on() and .off(). These methods are meant to simplify the usage of .bind(),.live() and .delegate() such that rather than relying on developers to know which of these options is the best to use, all developers can simply use .on() and .off() and we'll make the best logic decisions for what to use beneath the hood. Note: As of jQuery 1.7 all of these three methods (bind/live/delegate) use .on() and .off() when you call them, so calling these newer methods directly is advised.
Here's Ben Alman's really tiny pub/sub with my (minor) 1.7 updates from https://gist.github.com/1319216. The link to his gist with lots of useful comments is here: https://gist.github.com/661855
/* jQuery Tiny Pub/Sub - v0.7 - 10/27/2011 * http://benalman.com/ * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */ (function($) { var o = $({}); $.subscribe = function() { o.on.apply(o, arguments); }; $.unsubscribe = function() { o.off.apply(o, arguments); }; $.publish = function() { o.trigger.apply(o, arguments); }; }(jQuery));
Usage
// Super-basic example: function handle(e, a, b, c) { // `e` is the event object, you probably don't care about it. console.log(a + b + c); }; $.subscribe("/some/topic", handle); $.publish("/some/topic", [ "a", "b", "c" ]); // logs: abc $.unsubscribe("/some/topic", handle); // Unsubscribe just this handler // Or: $.subscribe("/some/topic", function(e, a, b, c) { console.log(a + b + c); }); $.publish("/some/topic", [ "a", "b", "c" ]); // logs: abc // Unsubscribe all handlers for this topic $.unsubscribe("/some/topic");
##Option 3: Using jQuery UI $.Observable
Note: $.Observables are currently still evolving and will not be available in jQuery UI until the jQueryUI Grid itself is released (http://wiki.jqueryui.com/w/page/34246941/Grid). The below is provided as a preview of what's being developed. Feel free to play around with the demos and submit any feedback or comments you may have about it.
The basic idea behind observables are that when objects/collections of data are changed or updated, events often need to be triggered to inform any observers of the change. This is a concept you see in a few different frameworks (Backbone's Collections for example). I believe the idea with this is that jQuery UI intend on applying this concept to UI components in particular (which should be very interesting to see).
Demo: http://jsfiddle.net/jUZmM/ Implem: http://view.jqueryui.com/grid/ui/jquery.ui.observable.js More information: http://wiki.jqueryui.com/w/page/47179578/Observable
/*$.observers/$.observables example by @addyosmani*/ // The array we would like observed var myData = []; // An 'observable' instance of myData var observer = $.observer(myData); // A simple data logger for when our observable myData changes function dataChange( data ){ console.log('New data arrived with ID ' + data[0].id + ' and value ' + data[0].title); } // Bind a callback to be executed when our observable instance of myData changes $(observer).bind("change", function ( e ) { dataChange( e.target.data ); }); // Insert a new record into the observable myData instance $.observable( myData ).insert({ id: myData.length + 1, title: 'test' });
##Option 4: Third-party Plugins
A number of jQuery plugins have been written which provide a pub/sub implementation free of reliance on .bind()/.trigger() (and consequently .on()/.off). Should you wish to use one of these solutions, I'm happy to recommend Peter Higgin's plugin available here: https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js.
Demo: http://jsfiddle.net/zkwra/
There are of course many, many library agnostic Pub/Sub implementations that have been written (Shameless plug: I've also done one https://github.com/addyosmani/pubsubz/), but this rough guide will be focusing on jQuery for the most part. Here's Peter's implementation and a demo:
;(function(d){ // the topic/subscription hash var cache = {}; // Publish some data on a named topic. d.publish = function(/* String */topic, /* Array? */args){ // topic: String - The channel to publish on // args: Array - The data to publish. Each array item is converted into an ordered // arguments on the subscribed functions. cache[topic] && d.each(cache[topic], function(){ this.apply(d, args || []); }); }; // Register a callback on a named topic. d.subscribe = function(/* String */topic, /* Function */callback){ // @topic: String - The channel to subscribe to // @callback: Function - The handler event. Anytime something is $.publish'ed on a // subscribed channel, the callback will be called with the published array as // ordered arguments. // // returns: Array - A handle which can be used to unsubscribe this // particular subscription. if(!cache[topic]){ cache[topic] = []; } cache[topic].push(callback); return [topic, callback]; // Array }; // Disconnect a subscribed function for a topic. d.unsubscribe = function(/* Array */handle){ // handle: Array - The return value from a $.subscribe call. var t = handle[0]; cache[t] && d.each(cache[t], function(idx){ if(this == handle[1]){ cache[t].splice(idx, 1); } }); }; })(jQuery);
// Publish stuff on '/some/topic'. Anything subscribed will be called // with a function signature like: function(a,b,c){ ... } $.subscribe("/some/topic", function(a, b, c){ console.log(a,b,c); }); $.publish("/some/topic", ["a","b","c"]);
nice!