Painless publish/subscribe with YUI custom events

2008-07-29 11:35:00

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]) {
  // Create a subscription for the passed object
  this.channels[channelName].subscribe(getSubscriberMethod(), obj);
YAHOO.util.Event.publish = function (channelName, paramObj) {
  if (!this.channels[channelName]) {
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 () {
    this, 'handlePublish');
  this.handlePublish = function (obj) {

var publisher = new function () {
  this.sendMessage = function (msg) {
      {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.


This is the blog for Matthew Eernisse. I currently work at Yammer as a developer, working mostly with JavaScript. All opinions expressed here are my own, not my employer's.


Previous posts

All previous posts ยป

This blog is a GeddyJS application.