A Comparison of JS Publish/Subscribe Approaches (v112)

Revision 112 of this benchmark created on


Description

A Comparison JS Publish/Subscribe Approaches

In this comparison I'm trying to focus on event inheritance feature present in PubSubJS and my implementation of the JQuery PubSub plugin called Subtopic.

In most PubSub implementations Subscribers and Publishers have a one to one relationship. This gives them a huge performance benefit, but can be a drawback when building complex decoupled applications as you have to wire every single event. In this comparison I'm looking at a pretty standard GUI like the one on google.com. We have a HEADER region, a tool region to the LEFT, a CONTENT region and a FOOTER. Each one of these regions are dependent on each other using PubSub to communicate. A MANAGER in each region is responsible for Loading/Unloading modules. A typical PubSub message for this application would look something like this:

"/APP/REGION/MODULE/EVENT"

Using a PubSub implementation that allows for inheritance allow us to create a subscriber for "/APP/REGION" that would listen to ALL events that occur within this region. Using the simpler implementations we would have to publish two events, one for the module and one for the region.

In this example each PubSub implementation has 4 subscribers. One for APP, REGION, MODULE and EVENT each. PubSubJS and JQuery Subscriber may invoke all four subscriber callbacks by a single publication to app/region/module/event where as the rest each has to publish 4 events.

More info

Compared:

Preparation HTML

<script src="https://raw.github.com/jrburke/requirejs/master/require.js" type="text/javascript">
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript">
</script>
<script src="https://raw.github.com/pmelander/Subtopic/master/jquery-subtopic.js"
type="text/javascript">
</script>
<script src="https://raw.github.com/phiggins42/bloody-jquery-plugins/55e41df9bf08f42378bb08b93efcb28555b61aeb/pubsub.js"
type="text/javascript">
</script>
<script src="https://raw.github.com/appendto/amplify/master/core/amplify.core.js"
type="text/javascript">
</script>
<script src="https://raw.github.com/maccman/spine/master/lib/spine.js" type="text/javascript">
</script>
<script src="https://raw.github.com/richardscarrott/ply/master/src/core.js" type="text/javascript">
</script>
<script type="text/javascript">
  window.iter = 0
  window.callback = function() {
    iter++
  };
  window.payload = {
    somekey: 'some value'
  };




  jQuery(function() {
    jQuery(window).on('/app', callback);
    jQuery(window).on('/app/region', callback);
    jQuery(window).on('/app/region/module', callback);
    jQuery(window).on('/app/region/module/event', callback);

    // PubSubJS doesn't expose any globals when in AMD environment
    require(['https://raw.github.com/mroderick/PubSubJS/master/src/pubsub.js'], function(PubSub){
      // add PubSub to global scope, to not pay the cost in every execution of the tests
      window.PubSub = PubSub;
      PubSub.subscribe('app', callback);
      PubSub.subscribe('app.region', callback);
      PubSub.subscribe('app.region.module', callback);
      PubSub.subscribe('app.region.module.event', callback);
    });
  
    jQuery.subscribe('/app', callback);
    jQuery.subscribe('/app/region', callback);
    jQuery.subscribe('/app/region/module', callback);
    jQuery.subscribe('/app/region/module/event', callback);

    Events.subscribe('/app', callback);
    Events.subscribe('/app/region', callback);
    Events.subscribe('/app/region/module', callback);
    Events.subscribe('/app/region/module/event', callback);

    amplify.subscribe('/app', callback);
    amplify.subscribe('/app/region', callback);
    amplify.subscribe('/app/region/module', callback);
    amplify.subscribe('/app/region/module/event', callback);

    Spine.bind('/app', callback);
    Spine.bind('/app/region', callback);
    Spine.bind('/app/region/module', callback);
    Spine.bind('/app/region/module/event', callback);

    Ply.core.listen('/app', callback);
    Ply.core.listen('/app/region', callback);
    Ply.core.listen('/app/region/module', callback);
    Ply.core.listen('/app/region/module/event', callback);
});
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
jQuery Events
jQuery(window)
  .trigger('/app', payload)
  .trigger('/app/region', payload)
  .trigger('/app/region/module', payload)
  .trigger('/app/region/module/event', payload);
ready
PubSubJS - async
PubSub.publish('app.region.module.event', payload);
ready
PubSubJS - sync
PubSub.publishSync('app.region.module.event', payload);
ready
Pure JS PubSub
Events.publish('/app', [payload]);
Events.publish('/app/region', [payload]);
Events.publish('/app/region/module', [payload]);
Events.publish('/app/region/module/event', [payload]);
ready
Amplify Pub/Sub
amplify.publish('/app', payload);
amplify.publish('/app/region', payload);
amplify.publish('/app/region/module', payload);
amplify.publish('/app/region/module/event', payload);
ready
Spine Events
Spine.trigger('/app', payload);
Spine.trigger('/app/region', payload);
Spine.trigger('/app/region/module', payload);
Spine.trigger('/app/region/module/event', payload);
ready
Ply Notify/Listen
Ply.core.notify('/app', window, payload);
Ply.core.notify('/app/region', window, payload);
Ply.core.notify('/app/region/module', window, payload);
Ply.core.notify('/app/region/module/event', window, payload);
ready
Subtopic for jQuery
$.publish('/app', [payload]);
$.publish('/app/region', [payload]);
$.publish('/app/region/module', [payload]);
$.publish('/app/region/module/event', [payload]);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.