Bluebird vs RSVP vs Native (v72)

Revision 72 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>
/**@license MIT-promiscuous-©Ruben Verborgh*/
(function (func, obj) {
  // Type checking utility function
  function is(type, item) { return (typeof item)[0] == type; }

  // Creates a promise, calling callback(resolve, reject), ignoring other parameters.
  function prPromise(callback, handler) {
    // The `handler` variable points to the function that will
    // 1) handle a .then(resolved, rejected) call
    // 2) handle a resolve or reject call (if the first argument === `is`)
    // Before 2), `handler` holds a queue of callbacks.
    // After 2), `handler` is a finalized .then handler.
    handler = function pendingHandler(resolved, rejected, value, queue, then, i) {
      queue = pendingHandler.q;

      // Case 1) handle a .then(resolved, rejected) call
      if (resolved != is) {
        return prPromise(function (resolve, reject) {
          queue.push({ p: this, r: resolve, j: reject, 1: resolved, 0: rejected });
        });
      }

      // Case 2) handle a resolve or reject call
      // (`resolved` === `is` acts as a sentinel)
      // The actual function signature is
      // .re[ject|solve](<is>, success, value)

      // Check if the value is a promise and try to obtain its `then` method
      if (value && (is(func, value) | is(obj, value))) {
        try { then = value.then; }
        catch (reason) { rejected = 0; value = reason; }
      }
      // If the value is a promise, take over its state
      if (is(func, then)) {
        function valueHandler(resolved) {
          return function (value) { then && (then = 0, pendingHandler(is, resolved, value)); };
        }
        try { then.call(value, valueHandler(1), rejected = valueHandler(0)); }
        catch (reason) { rejected(reason); }
      }
      // The value is not a promise; handle resolve/reject
      else {
        // Replace this handler with a finalized resolved/rejected handler
        handler = function (Resolved, Rejected) {
          // If the Resolved or Rejected parameter is not a function,
          // return the original promise (now stored in the `callback` variable)
          if (!is(func, (Resolved = rejected ? Resolved : Rejected)))
            return callback;
          // Otherwise, return a finalized promise, transforming the value with the function
          return prPromise(function (resolve, reject) { finalize(this, resolve, reject, value, Resolved); });
        };
        // Resolve/reject pending callbacks
        i = 0;
        while (i < queue.length) {
          then = queue[i++];
          // If no callback, just resolve/reject the promise
          if (!is(func, resolved = then[rejected]))
            (rejected ? then.r : then.j)(value);
          // Otherwise, resolve/reject the promise with the result of the callback
          else
            finalize(then.p, then.r, then.j, value, resolved);
        }
      }
    };
    // The queue of pending callbacks; garbage-collected when handler is resolved/rejected
    handler.q = [];

    // Create and return the promise (reusing the callback variable)
    callback.call(callback = { then:    function (resolved, rejected) { return handler(resolved, rejected); },
                               "catch": function (rejected)           { return handler(0,        rejected); } },
                  function (value)  { handler(is, 1,  value); },
                  function (reason) { handler(is, 0, reason); });
    return callback;
  }

  // Finalizes the promise by resolving/rejecting it with the transformed value
  function finalize(promise, resolve, reject, value, transform) {
    setTimeout(function () {
      try {
        // Transform the value through and check whether it's a promise
        value = transform(value);
        transform = value && (is(obj, value) | is(func, value)) && value.then;
        // Return the result if it's not a promise
        if (!is(func, transform))
          resolve(value);
        // If it's a promise, make sure it's not circular
        else if (value == promise)
          reject(TypeError());
        // Take over the promise's state
        else
          transform.call(value, resolve, reject);
      }
      catch (error) { reject(error); }
    }, 0);
  }

  // Export the main module
window.prPromise = prPromise;

  // Creates a resolved promise
  prPromise.resolve = ResolvedPromise;
  function ResolvedPromise(value) { return prPromise(function (resolve) { resolve(value); }); }

  // Creates a rejected promise
  prPromise.reject = function (reason) { return prPromise(function (resolve, reject) { reject(reason); }); };

  // Transforms an array of promises into a promise for an array
  prPromise.all = function (promises) {
    return prPromise(function (resolve, reject, count, values) {
      // Array of collected values
      values = [];
      // Resolve immediately if there are no promises
      count = promises.length || resolve(values);
      // Transform all elements (`map` is shorter than `forEach`)
      promises.map(function (promise, index) {
        ResolvedPromise(promise).then(
          // Store the value and resolve if it was the last
          function (value) {
            values[index] = value;
            --count || resolve(values);
          },
          // Reject if one element fails
          reject);
      });
    });
  };
})('f', 'o');
</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
promiscuous
function make() {
  return new prPromise(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.