Backbone's events new implementation (v2)

Revision 2 of this benchmark created on


Description

https://github.com/documentcloud/backbone/pull/601

Preparation HTML

<script src="https://raw.github.com/documentcloud/underscore/master/underscore-min.js"></script>
<script>
    Backbone = {}
  
    Backbone.NewEvents = {
  
      // Bind an event, specified by a string name, `ev`, to a `callback` function.
      // Passing `"all"` will bind the callback to all events fired.
      bind : function(ev, callback, context) {
        var calls   = this._callbacks || (this._callbacks = {});
        var events  = calls[ev] || (calls[ev] = {});
        var uid     = callback._uid || (callback._uid = _.uniqueId('e'));
        events[uid] = [ callback, context ];
        return this;
      },
  
      // Remove one or many callbacks. If `callback` is null, removes all
      // callbacks for the event. If `ev` is null, removes all bound callbacks
      // for all events.
      unbind : function(ev, callback) {
        var calls;
        if (!ev) {
          this._callbacks = {};
        } else if (calls = this._callbacks) {
          if (!callback) {
            calls[ev] = {};
          } else {
            delete calls[ev][callback._uid];
          }
        }
        return this;
      },
  
      // Trigger an event, firing all bound callbacks. Callbacks are passed the
      // same arguments as `trigger` is, apart from the event name.
      // Listening for `"all"` passes the true event name as the first argument.
      trigger : function(eventName) {
        var calls, events, args;
        if (!(calls = this._callbacks)) return this;
        args = Array.prototype.slice.call(arguments, 1);
        for(i in (events = calls[eventName])) {
          events[i][0].apply(events[i][1] || this, args);
        }
        for(i in (events = calls['all'])) {
          events[i][0].apply(events[i][1] || this, arguments);
        }
        return this;
      }
  
    };
  
    Backbone.OldEvents = {
  
      // Bind an event, specified by a string name, `ev`, to a `callback` function.
      // Passing `"all"` will bind the callback to all events fired.
      bind : function(ev, callback, context) {
        var calls = this._callbacks || (this._callbacks = {});
        var list  = calls[ev] || (calls[ev] = []);
        list.push([callback, context]);
        return this;
      },
  
      // Remove one or many callbacks. If `callback` is null, removes all
      // callbacks for the event. If `ev` is null, removes all bound callbacks
      // for all events.
      unbind : function(ev, callback) {
        var calls;
        if (!ev) {
          this._callbacks = {};
        } else if (calls = this._callbacks) {
          if (!callback) {
            calls[ev] = [];
          } else {
            var list = calls[ev];
            if (!list) return this;
            for (var i = 0, l = list.length; i < l; i++) {
              if (list[i] && callback === list[i][0]) {
                list[i] = null;
                break;
              }
            }
          }
        }
        return this;
      },
  
      // Trigger an event, firing all bound callbacks. Callbacks are passed the
      // same arguments as `trigger` is, apart from the event name.
      // Listening for `"all"` passes the true event name as the first argument.
      trigger : function(eventName) {
        var list, calls, ev, callback, args;
        var both = 2;
        if (!(calls = this._callbacks)) return this;
        while (both--) {
          ev = both ? eventName : 'all';
          if (list = calls[ev]) {
            for (var i = 0, l = list.length; i < l; i++) {
              if (!(callback = list[i])) {
                list.splice(i, 1); i--; l--;
              } else {
                args = both ? Array.prototype.slice.call(arguments, 1) : arguments;
                callback[0].apply(callback[1] || this, args);
              }
            }
          }
        }
        return this;
      }
  
    };
  
  var new_object = {};
  var old_object = {};
  
  _.extend(new_object, Backbone.NewEvents);
  _.extend(old_object, Backbone.OldEvents);
  
  var callbacks = [];
  for(var i = 0; i < 50; i++) {
    (function(i) {
       callbacks.push(function(a, b) { return a*b + i; });
    })(i);
  }
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
Add, trigger 100 times and remove callbacks to different events using new mehtods
_.each(callbacks, function(c, k) {
  new_object.bind('event_' + k, c);
});

_.each(callbacks, function(c, k) {
  for(var i = 0; i < 100; i++)
    new_object.trigger('event_' + k, k * 10, k);
});

_.each(callbacks, function(c, k) {
  new_object.unbind('event_' + k, c);
});
ready
Add, trigger 100 times and remove callbacks to different events using old mehtods
_.each(callbacks, function(c, k) {
  old_object.bind('event_' + k, c);
});

_.each(callbacks, function(c, k) {
  for(var i = 0; i < 100; i++)
    old_object.trigger('event_' + k, k * 10, k);
});

_.each(callbacks, function(c, k) {
  old_object.unbind('event_' + k, c);
});
ready
Add, trigger 100 times and remove callbacks to 3 events using new mehtods
_.each(callbacks, function(c, k) {
  new_object.bind('event_' + (k % 3), c);
});

_.each(callbacks, function(c, k) {
  for(var i = 0; i < 100; i++)
    new_object.trigger('event_' + (k % 3), k * 10, k);
});

_.each(callbacks, function(c, k) {
  new_object.unbind('event_' + (k % 3), c);
});
ready
Add, trigger 100 times and remove callbacks to 3 events using old mehtods
_.each(callbacks, function(c, k) {
  old_object.bind('event_' + (k % 3), c);
});

_.each(callbacks, function(c, k) {
  for(var i = 0; i < 100; i++)
    old_object.trigger('event_' + (k % 3), k * 10, k);
});

_.each(callbacks, function(c, k) {
  old_object.unbind('event_' + (k % 3), c);
});
ready
Add, trigger 100 times and remove callbacks to 1 event using new mehtods
_.each(callbacks, function(c, k) {
  new_object.bind('event', c);
});

_.each(callbacks, function(c, k) {
  for(var i = 0; i < 100; i++)
    new_object.trigger('event', k * 10, k);
});

_.each(callbacks, function(c, k) {
  new_object.unbind('event', c);
});
ready
Add, trigger 100 times and remove callbacks to 1 event using old mehtods
_.each(callbacks, function(c, k) {
  old_object.bind('event', c);
});

_.each(callbacks, function(c, k) {
  for(var i = 0; i < 100; i++)
    old_object.trigger('event', k * 10, k);
});

_.each(callbacks, function(c, k) {
  old_object.unbind('event', c);
});
ready

Revisions

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