Bluebird vs RSVP vs Native (v71)

Revision 71 of this benchmark created on


Description

A comparison of the two most highly performant promise implementations and the new native promises.

Preparation HTML

<script src="http://rsvpjs-builds.s3.amazonaws.com/rsvp-latest.js"></script>
<script src="http://petkaantonov.github.io/bluebird/cdn/bluebird-3.0.0.min.js"></script>

<script>var BPromise = Promise.noConflict();
if ((Promise + "").indexOf("native") < 0) alert("not native promises");
var deferred = {resolve: function() {}};
</script>

<script>
(function () {
        "use strict";

        var STATES = {
                PENDING: 'p',
                FULFILLED: 'f',
                REJECTED: 'r'
        };

        var isFunction = function (obj) {
                return obj && typeof obj === 'function';
        };

        var isObject = function (obj) {
                return obj && typeof obj === 'object';
        };

        var callWith = function () {
                var args = [].slice.call(arguments, 0);
                return function (fn) {
                        return fn.apply(null, args);
                };
        };

        var when = function (condition, fn) {
                return function () {
                        if (condition()) {
                                return fn.apply(null, arguments);
                        }
                };
        };

        var promise = function (fn) {
                var state = STATES.PENDING,
                        successCallbacksQueue = [],
                        errorCallbacksQueue = [],
                        value,
                        reason;
                
                var then = function (successCallback, errorCallback) {
                        var hasSuccessCallback = isFunction(successCallback);
                        var hasErrorCallback = isFunction(errorCallback);

                        return promise(function(resolve, reject) {
                                var onFulfill = function (value) {
                                        try {
                                                if (hasSuccessCallback) {
                                                        resolve(successCallback(value));
                                                } else {
                                                        resolve(value);
                                                }
                                        } catch (ex) {
                                                reject(ex);
                                        }
                                };
                                var onReject = function (reason) {
                                        try {
                                                if (hasErrorCallback) {
                                                        resolve(errorCallback(reason));
                                                } else {
                                                        reject(reason);
                                                }
                                        } catch (ex) {
                                                reject(ex);
                                        }
                                };

                                if (state === STATES.PENDING) {
                                        successCallbacksQueue.push(onFulfill);
                                        errorCallbacksQueue.push(onReject);
                                } else if (state === STATES.FULFILLED) {
                                        onFulfill(value)
                                } else if (state === STATES.REJECTED) {
                                        onReject(reason);
                                }
                        });
                };

                var isInState = function () {
                        var validStates = [].slice.call(arguments, 0);
                        return function () {
                                return validStates.indexOf(state) !== -1;
                        };
                };

                var resolve = when(isInState(STATES.PENDING), function (promiseValue) {
                        var doResolve = function (promiseValue) {
                                state = STATES.FULFILLED;
                                value = promiseValue;
                                successCallbacksQueue.forEach(callWith(value));
                        };
                        var handled = false;

                        if (promiseValue === self) {
                                reject(new TypeError());
                        } else if (!isFunction(promiseValue) && !isObject(promiseValue)) {
                                doResolve(promiseValue);
                        } else {
                                try {
                                        var then = promiseValue.then;
                                        if (isFunction(then)) {
                                                then.call(promiseValue, function(promiseValue) {
                                                        if (!handled) {
                                                                resolve(promiseValue);
                                                                handled = true;
                                                        }
                                                }, function (reason) {
                                                        if (!handled) {
                                                                reject(reason);
                                                                handled = true;
                                                        }
                                                });
                                        } else {
                                                doResolve(promiseValue);
                                                handled = true;
                                        }
                                } catch (ex) {
                                        if (!handled) {
                                                reject(ex);
                                        }
                                }
                        }
                });

                var reject = when(isInState(STATES.PENDING), function (rejectReason) {
                        state = STATES.REJECTED;
                        reason = rejectReason;
                        errorCallbacksQueue.forEach(callWith(reason));
                });

                if (fn) {
                        fn(resolve, reject);
                }

                var self = {
                        then: then,
                        catch: then.bind(this, null),
                        resolve: resolve,
                        reject: reject
                };

                return self;
        };


        if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
                define(function() {
                        return promise;
                });
        } else if (typeof module !== 'undefined' && module.exports) {
                module.exports = promise;
        } else {
                window.promise = promise;
        }

})();
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
Bluebird deferred
function make() {
  return new BPromise(function (resolve, reject) {
      resolve(1)
  })
}

make().then(function (v) { deferred.resolve(); })
ready
RSVP deferred
function make() {
  var defer = RSVP.defer()

    defer.resolve()

  return defer.promise
}

make().then(function () { deferred.resolve(); })
ready
Bluebird classic
function make() {
  return new BPromise(function (resolve, reject) {
      resolve(1)
  })
}

make().then(function (v) { deferred.resolve(); })
ready
RSVP classic
function make() {
  return new RSVP.Promise(function (resolve, reject) {
      resolve(1)
  })
}

make().then(function (v) { deferred.resolve(); })
ready
Native Promises
function make() {
  return new Promise(function (resolve, reject) {
      resolve(1)
  })
}

make().then(function (v) { deferred.resolve(); })
ready
My promises
function make() {
  return promise(function (resolve, reject) {
      resolve(1)
  })
}

make().then(function (v) { deferred.resolve(); }) 
ready

Revisions

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