PubSubJS vs. jQuery custom events
JavaScript performance comparison
Info
An attempt at showing that PubSubJS is faster than using jQuery custom evens for publish/subscribe style messaging.
It's certainly not as rich in features, and I am happy with that.
Preparation code
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
;(function(root, factory){
'use strict';
// CommonJS
if (typeof exports === 'object'){
module.exports = factory();
// AMD
} else if (typeof define === 'function' && define.amd){
define(factory);
// Browser
} else {
root.PubSub = factory();
}
}( ( typeof window === 'object' && window ) || this, function(){
'use strict';
var PubSub = {
name: 'PubSubJS',
version: '1.3.3'
},
messages = {},
lastUid = -1;
/**
* Returns a function that throws the passed exception, for use as argument for setTimeout
* @param { Object } ex An Error object
*/
function throwException( ex ){
return function reThrowException(){
throw ex;
};
}
function callSubscriberWithDelayedExceptions( subscriber, message, data ){
try {
subscriber( message, data );
} catch( ex ){
setTimeout( throwException( ex ), 0);
}
}
function callSubscriberWithImmediateExceptions( subscriber, message, data ){
subscriber( message, data );
}
function deliverMessage( originalMessage, matchedMessage, data, immediateExceptions ){
var subscribers = messages[matchedMessage],
callSubscriber = immediateExceptions ? callSubscriberWithImmediateExceptions : callSubscriberWithDelayedExceptions,
i, j;
if ( !messages.hasOwnProperty( matchedMessage ) ) {
return;
}
for ( i = 0, j = subscribers.length; i < j; i++ ){
callSubscriber( subscribers[i].func, originalMessage, data );
}
}
function createDeliveryFunction( message, data, immediateExceptions ){
return function deliverNamespaced(){
var topic = String( message ),
position = topic.lastIndexOf( '.' );
// deliver the message as it is now
deliverMessage(message, message, data, immediateExceptions);
// trim the hierarchy and deliver message to each level
while( position !== -1 ){
topic = topic.substr( 0, position );
position = topic.lastIndexOf('.');
deliverMessage( message, topic, data );
}
};
}
function messageHasSubscribers( message ){
var topic = String( message ),
found = messages.hasOwnProperty( topic ),
position = topic.lastIndexOf( '.' );
while ( !found && position !== -1 ){
topic = topic.substr( 0, position );
position = topic.lastIndexOf('.');
found = messages.hasOwnProperty( topic );
}
return found;
}
function publish( message, data, sync, immediateExceptions ){
var deliver = createDeliveryFunction( message, data, immediateExceptions ),
hasSubscribers = messageHasSubscribers( message );
if ( !hasSubscribers ){
return false;
}
if ( sync === true ){
deliver();
} else {
setTimeout( deliver, 0 );
}
return true;
}
/**
* PubSub.publish( message[, data] ) -> Boolean
* - message (String): The message to publish
* - data: The data to pass to subscribers
* Publishes the the message, passing the data to it's subscribers
**/
PubSub.publish = function( message, data ){
return publish( message, data, false, PubSub.immediateExceptions );
};
/**
* PubSub.publishSync( message[, data] ) -> Boolean
* - message (String): The message to publish
* - data: The data to pass to subscribers
* Publishes the the message synchronously, passing the data to it's subscribers
**/
PubSub.publishSync = function( message, data ){
return publish( message, data, true, PubSub.immediateExceptions );
};
/**
* PubSub.subscribe( message, func ) -> String
* - message (String): The message to subscribe to
* - func (Function): The function to call when a new message is published
* Subscribes the passed function to the passed message. Every returned token is unique and should be stored if
* you need to unsubscribe
**/
PubSub.subscribe = function( message, func ){
// message is not registered yet
if ( !messages.hasOwnProperty( message ) ){
messages[message] = [];
}
// forcing token as String, to allow for future expansions without breaking usage
// and allow for easy use as key names for the 'messages' object
var token = String(++lastUid);
messages[message].push( { token : token, func : func } );
// return token for unsubscribing
return token;
};
/**
* PubSub.unsubscribe( tokenOrFunction ) -> String | Boolean
* - tokenOrFunction (String|Function): The token of the function to unsubscribe or func passed in on subscribe
* Unsubscribes a specific subscriber from a specific message using the unique token
* or if using Function as argument, it will remove all subscriptions with that function
**/
PubSub.unsubscribe = function( tokenOrFunction ){
var isToken = typeof tokenOrFunction === 'string',
key = isToken ? 'token' : 'func',
succesfulReturnValue = isToken ? tokenOrFunction : true,
result = false,
m, i;
for ( m in messages ){
if ( messages.hasOwnProperty( m ) ){
for ( i = messages[m].length-1 ; i >= 0; i-- ){
if ( messages[m][i][key] === tokenOrFunction ){
messages[m].splice( i, 1 );
result = succesfulReturnValue;
// tokens are unique, so we can just return here
if ( isToken ){
return result;
}
}
}
}
}
return result;
};
return PubSub;
}));
</script>
<script>
var callback1 = function() {};
var callback2 = function() {};
var payload = {
somekey: 'some value'
};
var body;
// let's use jQuery.ready to make sure that the DOM is ready,
// before trying to work with it
jQuery(function() {
// we'll use the body element to exchange messages for jQuery
// if using deeper nested elements, jQuery will be slower, as custom events bubble
body = $('body');
// subscribe our callback1 function to the custom event for jQuery, only once
body.bind('my-event', callback1);
// subscribe our callback2 function to the message for PubSub
PubSub.subscribe('my-event', callback2)
});
</script>
Preparation code output
Test runner
Warning! For accurate results, please disable Firebug before running the tests. (Why?)
Java applet disabled.
| Test | Ops/sec | |
|---|---|---|
jQuery - trigger |
|
pending… |
PubSub - publish - asyncronous |
|
pending… |
PubSub - publish - syncronous |
|
pending… |
Compare results of other browsers
Revisions
You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:
- Revision 1: published by Morgan Roderick
- Revision 3: published by Morgan Roderick
- Revision 4: published
- Revision 5: published by Stephen Crosby
- Revision 6: published by Andrew Betts
- Revision 8: published
- Revision 11: published
- Revision 12: published by Morgan Roderick
- Revision 18: published
- Revision 19: published
- Revision 20: published
- Revision 21: published
- Revision 22: published by Richard and last updated
- Revision 24: published by Hiroshi Kuwabara
- Revision 25: published
- Revision 26: published by s0ber
- Revision 30: published by Jakeb
- Revision 31: published by Jakeb
- Revision 34: published by pmelande
- Revision 36: published by pmelande
- Revision 37: published by pmelande
- Revision 39: published by Jakeb
- Revision 40: published by Jakeb
- Revision 41: published by pmelande
- Revision 43: published by Morgan Roderick
- Revision 44: published by Jakeb
- Revision 47: published by asyraf
- Revision 50: published by pmelande
- Revision 51: published by pmelande
- Revision 52: published by Jakeb
- Revision 53: published
- Revision 54: published
- Revision 55: published
- Revision 56: published by Anas Nakawa
- Revision 57: published
- Revision 58: published
- Revision 59: published by Burke
- Revision 60: published by Tobias Haustein
- Revision 62: published by vlazar
- Revision 64: published by Stuart
- Revision 66: published by Brandon Papworth
- Revision 67: published
- Revision 68: published
- Revision 69: published
- Revision 70: published
- Revision 73: published
- Revision 74: published
- Revision 75: published by Wojciech Dłubacz
- Revision 76: published
- Revision 77: published
- Revision 78: published by Wojciech Dłubacz
- Revision 79: published
- Revision 80: published by Sparda
- Revision 82: published
- Revision 83: published by Bruno
- Revision 85: published
- Revision 86: published
- Revision 87: published
- Revision 88: published
- Revision 89: published
- Revision 90: published
- Revision 91: published
- Revision 92: published by Caleb Gilbert
- Revision 93: published
- Revision 94: published
- Revision 95: published
- Revision 97: published by Brandon Papworth
- Revision 99: published
- Revision 100: published
- Revision 101: published
- Revision 102: published
0 comments