Painless publish/subscribe with YUI custom events

We're using YUI now at Seesmic. We were using my Fleegix.js library, but as more developers come on board I think it's better to use something mainstream. YUI also has a large array of well-tested UI elements (Overlay, TabView, etc.) that make rapid development easier.

So far the transition has been relatively painless. YUI is massively frameworky and kind of ponderous -- but it is well documented, and it's pretty easy to get it to do what you need it to.

The glaring exception so far has been their implementation of publish/subscribe. Sure, YUI has custom events . Any toolkit for building real apps (as opposed to Ajaxy pages or sites) needs it for keeping UI components decoupled.

Sadly, in the case of YUI, pub/sub is a bit of a mess -- custom events is a weird mish-mosh of function, scope, and params flying around, with no over-arching design that I can discern.

I keep thinking that the Yahoo (no, I won't put that fucking exclamation mark on the end -- that's! completely! stupid!) JS guys have to be smart, but when I see something like this I can't help but wonder what drugs they were on. The over-the-top ad-hoc-ness bears a strong resemblance to a good old-fashioned game of Fizzbin .

I have used other pub/sub systems that don't suck. Dojo , for all its other imperfections, has a really uber eventing system with really nice pub/sub -- which is of course why I cloned its API for Fleegix.js's event.publish and event.subscribe .

Thankfully, JavaScript's meta-programming facilities make it pretty easy to consign YUI's Bizarro World custom-events API to code-purgatory. A simple wrapper around the CustomEvent stuff gives you a simple and sane pub/sub interface.

Here's the code:

   YAHOO.util.Event.channels = {}; 
YAHOO.util.Event.subscribe =
   
    function
   
   (channelName, obj, listenMethod) {
   
    var
   
   getSubscriberMethod =
   
    function
   
   () {
   
    return
   
   
    function
   
   (type, args, scopeObj) {
      scopeObj[listenMethod].apply(scopeObj, args);
    }   
  }
   
    if
   
   (!
   
    this
   
   .channels[channelName]) {
   
    this
   
   .createChannel(channelName);
  }
   
    // Create a subscription for the passed object
   
   
    this
   
   .channels[channelName].subscribe(getSubscriberMethod(), obj);
};
YAHOO.util.Event.publish =
   
    function
   
   (channelName, paramObj) {
   
    if
   
   (!
   
    this
   
   .channels[channelName]) {
   
    this
   
   .createChannel(channelName);
  }
   
    this
   
   .channels[channelName].fire(paramObj);
};
YAHOO.util.Event.createChannel =
   
    function
   
   (channelName) {
   
    this
   
   .channels[channelName] =
   
    new
   
   YAHOO.util.CustomEvent(channelName,
   
    this
   
   );
};
  
 

This creates two methods you can use in the YAHOO.util.Event namespace -- publish , and subscribe . Calling either one of these will automatically create the specified channel if it doesn't exist.

You use it like this:

    var
   
   subscriber =
   
    new
   
   
    function
   
   () {
  YAHOO.util.Event.subscribe(
   
    'someChannelName'
   
   ,
   
    this
   
   ,
   
    'handlePublish'
   
   );
   
    this
   
   .handlePublish =
   
    function
   
   (obj) {
    alert(obj.message);
  };  
}
   
    var
   
   publisher =
   
    new
   
   
    function
   
   () {
   
    this
   
   .sendMessage =
   
    function
   
   (msg) {
    YAHOO.util.Event.publish(
   
    'someChannelName'
   
   ,
      {message: msg});
  }
};

publisher.sendMessage(
   
    'foo'
   
   );
   
    // Alerts 'foo'
   
  
 

This is a really minimal example, but you get the idea.

Basically this just lets you send an arbitrary package of stuff (data, functions -- anything you can stick in a JavaScript object) to a named channel, and all the interested objects listening on that channel will receive it. Nice, simple pub/sub -- easy as pie.

I haven't really banged on this very heavily, so there may be some bumps in the road, but so far it's working like a champ -- publishing messages across the app, and staying out of the way.

Obviously too, there's a tiny bit of overhead added working through this wrapper -- but pub/sub is for macro-level events that function across your entire app, not micro-eventing within single components. The performance implications of using this wrapper should be pretty negligible.

It's a small price to pay for avoiding a game of Fizzbin.