Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Created October 14, 2011 22:47
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ryanflorence/1288582 to your computer and use it in GitHub Desktop.
Save ryanflorence/1288582 to your computer and use it in GitHub Desktop.
Simple Pub/Sub
!function () {
var channels = {};
this.subscribe = function (channel, subscription) {
if (!channels[channel]) channels[channel] = [];
channels[channel].push(subscription);
};
this.publish = function (channel) {
if (!channels[channel]) return;
var args = [].slice.call(arguments, 1);
for (var i = 0, l = channels[channel].length; i < l; i++) {
channels[channel][i].apply(this, args);
}
};
}.call(this);
subscribe('foo', function (a, b) {
console.log(a, b);
});
publish('foo', 1, 2);
@addyosmani
Copy link

Hey @rpflorence. The code above looks fine, but I just had one question: the implementation currently on the large-scale JS post uses pub/sub via the mediator pattern rather than via a "direct" pub/sub system (that specific approach is used for a reason mentioned in the broader article). Would you like to wrap your updates into a mediator as per the original (http://addyosmani.com/largescalejavascript/) or were you suggesting just using pub/sub?

@ryanflorence
Copy link
Author

@addyosmani - I've already linked some people to this gist, so I made a new gist.

Here's a simple mediator function you're talking about: https://gist.github.com/1290072

My full blown publisher that I use in production is here: http://ryanflorence.com/publisher.js/ and a demo app that uses it: http://ryanflorence.com/non-trivial-js/

@nijikokun
Copy link

A better solution, and still same size:

!function () {
  var channels = {};

  this.subscribe = function (channel, subscription) {
      if(!channel) return;
      (!channels[channel] ? (channels[channel] = []) : channels[channel]).push(subscription);
  };

  this.publish = function (channel) {
    if (!channels[channel] || !channel) return;
    var args = [].slice.call(arguments, 1);
    for (var i = 0, l = channels[channel].length; i < l; i++)
      channels[channel][i].apply(this, args);
  };
}.call(this);

@ryanflorence
Copy link
Author

Why is that better? By more confusing, then sure, you nailed it.

  1. Why check if (!channel) return; in subscribe?
  2. Why check !channel in if (!channels[channel] || !channel) return; in publish?

I stand by my code as being far more readable, and more performant (ternary assignments like the one you've described are the slowest).

If speed was my goal for that method, I'd do this:

this.subscribe = function (channel, subscription) {
  (channels[channel] || (channels[channel] = [])).push(subscription);
};

But the purpose of this code is to explain the pattern, not how to make faster an already fast operation.

@nijikokun
Copy link

First, I added that because if you pass along: '' or null, it will either a) not even work, b) throw an error regarding a null, that prevents it.
Instead you could add a throw "Missing channel on publish" however, as a programmer you should have known that.

Since speed and performance is not the goal here, as you stated it was the "simplest" on twitter, I simplified it further, and corrected errors that you missed.

And, since you state speed is not your goal, why would you even bring it up? Also, Performance? Please read this:
http://stackoverflow.com/questions/2586842/javascript-if-else-or-ternary-operator-is-faster

@ryanflorence
Copy link
Author

Sorry for the original response, just re-read it and I sound like a jerk. Please forgive me.

You don't want to throw an error when you publish a channel with no subscriptions, you'd be throwing errors like crazy because you aren't always subscribing to everything. That's the beauty of pub sub, tell the world you're doing something, but don't require anybody to do anything with it, nor require every module to be operating properly. A module can fail, but it doesn't affect the rest of your app.

I still don't see the "errors I missed" nor do I see how your solution is simpler :\ How is a confusing ternary expression simpler than a quick if statement?

@nijikokun
Copy link

if statements require a variable to be instanced, the ternary solves that. your personal quip about the (||) is better than mine and simpler, I was simplifying the code to not instance variables.

The errors were not checking variables, that is up to the creator I suppose, you can exclude it, just a simple "protection" layer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment