promise comparisons

JavaScript performance comparison

Revision 55 of this test case created by Brian Cavalier

Info

This is a comparison of different promise libraries, performing the most basic tasks of creating a promise, adding a then handler and then resolving the promise.

Preparation code

<script src="https://rawgithub.com/petkaantonov/bluebird/master/js/bluebird.js">
</script>
<script>
  window.BluebirdPromise = window.Promise.noConflict();
console.log(BluebirdPromise);
</script>
<script src="https://rawgithub.com/calvinmetcalf/lie/mutation/dist/lie.noConflict.js"></script><script src="http://rsvpjs-builds.s3.amazonaws.com/rsvp-latest.js"></script>
<script src="//rawgithub.com/calvinmetcalf/catiline/mutation/dist/catiline.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/q.js/0.9.6/q.min.js">
</script>
<script>
  window.define = function(factory) {
    try {
      delete window.define;
    } catch (e) {
      window.define = void 0;
    } // IE
    window.when = factory();
  };
  window.define.amd = {};
</script>
<script src="https://rawgithub.com/cujojs/when/master/when.js">
</script><script>
var worker = cw({
    init:function(self){
        self.on('ping',function(d){
            self.fire('pong',d);
        });
}
    });
</script>
<script>
(function(e){if("function"==typeof bootstrap)bootstrap("promise",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makePromise=e}else"undefined"!=typeof window?window.Promise=e():global.Promise=e()})(function(){var define,ses,bootstrap,module,exports;
return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var nextTick, handlerQueue, bind, uncurryThis, call, MutationObserver, undef;

bind = Function.prototype.bind;
uncurryThis = bind.bind(bind.call);
call = uncurryThis(bind.call);

module.exports = Promise;

Promise.resolve = resolve;
Promise.cast    = cast;
Promise.reject  = reject;
Promise.all     = all;
Promise.race    = race;

// Return a pending promise whose fate is determined by resolver
function Promise(resolver) {
        var value, handlers = [];

        this.when = function(onFulfilled, onRejected, resolve) {
                handlers ? handlers.push(deliver) : enqueue(deliver);

                function deliver() {
                        value.when(onFulfilled, onRejected, resolve);
                }
        };

        // Call the resolver to seal the promise's fate
        try {
                resolver(promiseResolve, promiseReject);
        } catch(e) {
                promiseReject(e);
        }

        // Reject with reason verbatim
        function promiseReject(reason) {
                promiseResolve(new Rejected(reason));
        }

        // Resolve with a value, promise, or thenable
        function promiseResolve(x) {
                if(!handlers) {
                        return;
                }

                var queue = handlers;
                handlers = undef;

                enqueue(function () {
                        value = coerce(x);
                        for(var i=0; i<queue.length; ++i) {
                                queue[i]();
                        }
//                      queue.forEach(function(handler) {
//                              handler();
//                      });
                });
        }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
        var self = this;
        return new Promise(function(resolve) {
                self.when(onFulfilled, onRejected, resolve);
        });
};

Promise.prototype.done = function(task) {
        this.when(task, function(e) {
                enqueue(function() { throw e; });
        }, noop);
}

Promise.prototype['catch'] = function(onRejected) {
        return this.then(null, onRejected);
};

// Coerce x to a promise
function coerce(x) {
        if(x instanceof Promise) {
                return x;
        }

        if (!(x === Object(x) && 'then' in x)) {
                return new Fulfilled(x);
        }

        return new Promise(function(resolve, reject) {
                try {
                        var untrustedThen = x.then;

                        if(typeof untrustedThen === 'function') {
                                call(untrustedThen, x, resolve, reject);
                        } else {
                                resolve(new Fulfilled(x));
                        }
                } catch(e) {
                        reject(e);
                }
        });
}

function Fulfilled(value) {
        this.value = value;
}

Fulfilled.prototype = Object.create(Promise.prototype);
Fulfilled.prototype.when = function(onFulfilled, _, resolve) {
        try {
                resolve(typeof onFulfilled == 'function'
                        ? onFulfilled(this.value) : this);
        } catch (e) {
                resolve(new Rejected(e));
        }
};

function Rejected(reason) {
        this.value = reason;
}

Rejected.prototype = Object.create(Promise.prototype);
Rejected.prototype.when = function(_, onRejected, resolve) {
        try {
                resolve(typeof onRejected == 'function'
                        ? onRejected(this.value) : this);
        } catch (e) {
                resolve(new Rejected(e));
        }
};

function cast(x) {
        return x instanceof Promise ? x : resolve(x);
}

function resolve(x) {
        return new Promise(function(resolve) {
                resolve(x);
        });
}

function reject(x) {
        return new Promise(function(_, reject) {
                reject(x);
        });
}

// Return a promise that will fulfill after all promises in array
// have fulfilled, or will reject after one promise in array rejects
function all(array) {
        return new Promise(resolveAll);

        function resolveAll(resolve, reject) {
                var results, toResolve = array.length;

                if(!toResolve) {
                        resolve(results);
                        return;
                }

                results = [];
                array.forEach(function(item, i) {
                        cast(item).then(function(value) {
                                results[i] = value;

                                if(!--toResolve) {
                                        resolve(results);
                                }
                        }, reject);
                });
        }
}

function race(array) {
        return new Promise(resolveRace);

        function resolveRace(resolve, reject) {
                array.forEach(function(item) {
                        cast(item).then(resolve, reject);
                });
        }
}

function noop() {}

handlerQueue = [];
function enqueue(task) {
        if(handlerQueue.push(task) === 1) {
                nextTick(drainQueue);
        }
}

function drainQueue() {
        var task, i = 0, queue = handlerQueue;

        handlerQueue = [];
        while(task = queue[i++]) {
                task();
        }
}

// Sniff "best" async scheduling option
/*global process,window,document*/
if (typeof process === 'object' && process.nextTick) {
        nextTick = process.nextTick;
} else if(typeof window !== 'undefined' && (MutationObserver = window.MutationObserver || window.WebKitMutationObserver)) {
        nextTick = (function(document, MutationObserver, drainQueue) {
                var el = document.createElement('div');
                new MutationObserver(drainQueue).observe(el, { attributes: true });

                return function() {
                        el.setAttribute('x', 'x');
                };
        }(document, MutationObserver, drainQueue));
} else {
        nextTick = function(t) { setTimeout(t, 0); };
}
},{}]},{},[1])
(1)
});
;</script>

Preparation code output

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
lie
// async test
var d = lie();
var code = 'lie' + Math.random();
function eventFunc(e) {
  if (e === code) {
worker.off('pong');
    d.resolve();
}
}
worker.on('pong',eventFunc);
d.promise.then(function() {
  deferred.resolve()
})
worker.fire('ping',code);
pending…
when
// async test
var code = 'when' + Math.random();
var promise = when.promise(function(resolve) {
  function eventFunc(e) {
    if (e === code) {
      worker.off('pong');
      resolve();
    }
  }
  worker.on('pong',eventFunc);
});

promise.then(function() {
  deferred.resolve()
})
worker.fire('ping',code);
pending…
RSVP
// async test
var d = RSVP.defer();
var code = 'rsvp' + Math.random();
function eventFunc(e) {
  if (e === code) {
worker.off('pong');
    d.resolve();
}
}
worker.on('pong',eventFunc);
d.promise.then(function() {
  deferred.resolve()
})
worker.fire('ping',code);
pending…
q
// async test
var d = Q.defer()
var code = 'q' + Math.random();
function eventFunc(e) {
  if (e === code) {
worker.off('pong');
    d.resolve();
}
}
worker.on('pong',eventFunc);
d.promise.then(function() {
  deferred.resolve()
})
worker.fire('ping',code);
pending…
catiline
// async test
var d = cw.deferred()
var code = 'cw' + Math.random();
function eventFunc(e) {
  if (e === code) {
worker.off('pong');
    d.resolve();
}
}
worker.on('pong',eventFunc);
d.promise.then(function() {
  deferred.resolve()
})
worker.fire('ping',code);
pending…
truth
// async test
var code = 'truth' + Math.random();
var promise = new Promise(function(resolve) {
  function eventFunc(e) {
    if (e === code) {
      worker.off('pong');
      resolve();
    }
  }
  worker.on('pong',eventFunc);
});

promise.then(function() {
  deferred.resolve()
})
worker.fire('ping',code);
pending…
bluebird
// async test
console.log(BluebirdPromise);
var code = 'bluebird' + Math.random();
var promise = new BluebirdPromise(function(resolve) {
  function eventFunc(e) {
    if (e === code) {
      worker.off('pong');
      resolve();
    }
  }
  worker.on('pong',eventFunc);
});

promise.then(function() {
  deferred.resolve()
})
worker.fire('ping',code);
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:

0 comments

Add a comment