Skip to content

Instantly share code, notes, and snippets.

@addyosmani
Created December 5, 2012 16:04
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save addyosmani/4216912 to your computer and use it in GitHub Desktop.
Save addyosmani/4216912 to your computer and use it in GitHub Desktop.
Pub/Sub latest
/*
A basic implementation of the Publisher/Subscriber design pattern.
Developed by @addyosmani and @integralist
Example Usage:
// Create subscriber function to be called when topic publishes an event
var testSubscriber = function (topics, data) {
console.log(topics + ': ' + data);
};
// Subscribe 'testSubscriber' to the event 'example1'
var testSubscription = pubsub.subscribe('example1', testSubscriber);
// Trigger the 'example1' event multiple times with different data each time
pubsub.publish('example1', 'hello world!');
pubsub.publish('example1', ['test','a', 'b', 'c']);
pubsub.publish('example1', [{ 'color':'blue' }, { 'text' : 'hello' }]);
// Unsubscribe 'testSubscription' from being notified of published events
setTimeout(function(){
pubsub.unsubscribe(testSubscription);
}, 0);
// This event will be published but 'testSubscription' will no longer receive a notification of it
pubsub.publish('example1', 'hello again!');
*/
define(function(){
var doc = document;
var topics = {};
var id = -1;
var pubsub = {};
/*
Subscribe to a specific topic and specify a callback function
to be executed when the topic triggers an event
@param topics {String} the name of the topic to add the subscriber to
@param fn {Function} the function to be called when an event for this topic is triggered
@return token {String} the unique id to be associated with the subscriber
*/
pubsub.subscribe = function (topic, fn) {
/*
If the specified topic doesn't exist on the object
then add it as a new property and set it to an empty Array
*/
if (!topics[topic]) {
topics[topic] = [];
}
// Create a unique id to be associated with the subscriber
var token = (++id).toString();
// Store the token and the subscriber (function) in this topic
topics[topic].push({
token: token,
fn: fn
});
// Return the unique id for this subscriber
return token;
};
/*
Unsubscribe the specified subscriber (specified by the unique token associated to a subscriber)
@param token {String} the unique id for the subscriber
@Return false {Boolean} nothing else to happen so just return false
*/
pubsub.unsubscribe = function (token) {
// Loop through all topics...
for (var m in topics) {
// For each topic...
if (topics[m]) {
// Loop through each subscriber...
for (var i = 0, j = topics[m].length; i < j; i++) {
// If the current subscriber's token matches the token passed to the function...
if (topics[m][i].token === token) {
// Then remove it...
topics[m].splice(i, 1);
// And return the subscriber's unique id as a reference
return token;
}
}
}
}
return false;
};
/*
Publish an event for the specified topic, which will trigger all associated subscriber functions to execute
@param topic {String} the topic name
@param data {Multiple} data is passed through to the subscriber function to process (data type can be anything, String/Array/Object whatever!)
@Return true {Boolean} return true so we know the function was executed successfully
*/
pubsub.publish = function (topic, data) {
// If the topic specified cannot be found then return the function early (no point continuing)
if (!topics[topic]) {
return false;
}
// Asynchronously execute each subscriber function
setTimeout(function(){
// Cache the topic
var subscribers = topics[topic],
// Cache the length of subscribers for the topic
// Note: If the topic has no subscribers then the length will be equal to zero and the following loop will not run
len = topics[topic].length;
// Loop through each subscriber...
while (len--) {
// And execute the subscriber's associated function
subscribers[len].fn(topic, data);
}
}, 0);
return true;
};
return pubsub;
});
@timkg
Copy link

timkg commented Dec 6, 2012

Hi, quick question - in the example, you use a setTimeout callback to unsubscribe testSubscription. Why is that? My understanding is that async function calls such as a timer callback are pushed to the end of the event queue, being executed after the current call stack exits. Why would that be desired in the context of the example?

@timkg
Copy link

timkg commented Dec 6, 2012

Ok, next time I should read the whole code before asking questions. :) You execute the registered callbacks asynchronously (line 106), so calling unsubscribe directly would delete the subscription before the subscribers are executed.

Next question - why do you execute the subscribers asynchronously? Because they might be quite heavy (or just many of them), so calling them asynchronously works as a safeguard against potential performance problems or UI blocking? Thanks in advance!

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