exoskeleton vs backbone events (v13)

Revision 13 of this benchmark created by Chris Coniglio on


Preparation HTML

<script src='//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.2.0/lodash.underscore.js'></script>
<script src='//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js'></script>
<script>var bbEvents = Backbone.Events;</script>
<script>var extend = _.extend; window._ = null;</script>
<script src="https://github.com/paulmillr/exoskeleton/releases/download/0.5.1/exoskeleton.js"></script>
<script>var exosEvents = Exoskeleton.Events;</script>
<script>function dmafevents (context) {
    var dictionary = {
            names: {}
        },
        separator = /:/,
        currentEvent = "",
        doStop;

    function dispatch (eventName) {
        var oldstop,
            listeners,
            listener,
            ce;

        if (eventName || typeof eventName === "string") {
            oldstop = doStop;
            ce = currentEvent;
            currentEvent = eventName;
            listeners = getListeners(eventName);
            doStop = false;
            for (var i = 0, ii = listeners.length; i < ii; i++) {
                listener = listeners[i];
                listener.apply(this, arguments);
                if (doStop) {
                    break;
                }
            }
            doStop = oldstop;
            currentEvent = ce;
        }
    }
    function getListeners (name) {
        var names = name.split(separator),
            _events = dictionary, //make dictionary local
            item,
            listeners = [];
        for (var i = 0, ii = names.length; i < ii; i++) {
            item = _events.names && _events.names[names[i]];
            if (item) {
                listeners = listeners.concat(item.listeners || []);
                _events = _events.names[names[i]]; // stop down in the event heirarchy
            } else {
                break;
            }
        }
        return listeners;
    }
    function stop () {
        doStop = 1;
    }
    function addEventListener (eventName, listener) {
        var names = eventName.split(separator),
            _events = dictionary; // make dictionary local
        for (var i = 0, ii = names.length; i < ii; i++) {
            _events = _events.names;
            if (_events.hasOwnProperty(names[i]) && _events[names[i]] !== void 0) { // here's that void 0 thing again
                _events = _events[names[i]]; // step down one level in the event heirarchy
            } else {
                _events[names[i]] = {
                    names: {}
                }; // create the missing level in the heirarchy
                _events = _events[names[i]];
            }
        }
        _events.listeners = _events.listeners || [];
        for (i = 0, ii = _events.listeners.length; i < ii; i++) {
            if (_events.listeners[i] === listener) { // if the listener already has been added we just return it
                return listener;
            }
        }
        _events.listeners.push(listener);
        return listener;
    }
    function once (eventName, listener) {
        var volatileListener = function () {
            removeEventListener(eventName, volatileListener);
            return listener.apply(this, arguments);
        };
        return addEventListener(eventName, volatileListener);
    }
    function removeEventListener (eventName, listener) {
        var names = eventName.split(separator),
            _events,
            key,
            spliceArguments,
            i, ii, j, jj,
            currentEvents = [dictionary];

        for (i = 0, ii = names.length; i < ii; i++) {
            for (j = 0; j < currentEvents.length; j++) {
                spliceArguments = [j, 1];
                _events = currentEvents[j].names;
                if (_events[names[i]]) {
                    spliceArguments.push(_events[names[i]]);
                }
                currentEvents.splice.apply(currentEvents, spliceArguments);
            }
        }
        for (i = 0, ii = currentEvents.length; i < ii; i++) { // will this ever be run? Won't currentEvents.length always be 0 at this point?
            _events = currentEvents[i];
            while (_events.names) {
                if (listener) {
                    if (_events.listeners) {
                        for (j = 0, jj = _events.listeners.length; j < jj; j++) {
                            if (_events.listeners[j] === listener) {
                                _events.listeners.splice(j, 1);
                                break;
                            }
                        }
                        if (!_events.listeners.length) {
                            delete _events.listeners;
                        }
                    }
                    for (key in _events.names) {
                        if (_events.names.hasOwnProperty(key) && _events.names[key].listeners) {
                            var listeners = _events.names[key].listeners;
                            for (j = 0, jj = listeners.length; j < jj; j++) {
                                if (listeners[j] === listener) {
                                    listeners.splice(j, 1);
                                    break;
                                }
                                if (!listeners.length) {
                                    delete _events.names[key].listeners;
                                }
                            }
                        }
                    }
                } else {
                    delete _events.listeners;
                    for (key in _events.names) {
                        if (_events.names.hasOwnProperty(key) && _events.names[key].listeners) {
                            delete _events.names[key].listeners;
                        }
                    }
                }
                _events = _events.names;
            }
        }
    }
    function current () {
        return currentEvent;
    }
    context.addEventListener = context.on = addEventListener;
    context.removeEventListener = context.off = removeEventListener;
    context.getListeners = getListeners;
    context.getCurrentEvent = current;
    context.stopEvents = stop;
    context.once = once;
    context.dispatch = dispatch;
    context._events = dictionary;
    context._newEventSystem = dmafevents;
}</script>

Setup

var numEvents = 3;
    var numListeners = 100;
    var bb = extend({x:0}, bbEvents);
    var exos = extend({x:0}, exosEvents);
    var dmaf = {x: 0};
    dmafevents(dmaf);
    
    [bb, exos, dmaf].forEach(function(obj) {
      obj.on('event', function() { obj.x++; });
    });

Test runner

Ready to run.

Testing in
TestOps/sec
Exoskeleton
exos.trigger('event', 1, 2, 3);
ready
Backbone
bb.trigger('event', 1, 2, 3);
ready
DMAF
dmaf.dispatch('event', 1, 2, 3);
ready

Revisions

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