promise comparisons (v145)

Revision 145 of this benchmark created by dfgdsdfsfdsdfgq on


Description

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.

Recent benchmarks deliberately cripple bluebird but didn't force the same method on other libraries.

*Added ES6 Promise polyfill *Added kew *Added MyDeferred

Preparation HTML

<script src="http://sk.orion.eclipse.org:8080/orion/Deferred.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="https://rawgithub.com/cho45/jsdeferred/master/jsdeferred.js"></script>
<script src="https://rawgithub.com/petkaantonov/bluebird/master/js/browser/bluebird.js"></script>
<script>
  window.BluebirdPromise = window.Promise.noConflict();
  delete window.Promise;
</script>
<script src="https://rawgithub.com/calvinmetcalf/lie/master/dist/lie.noConflict.js"></script><script src="http://rsvpjs-builds.s3.amazonaws.com/rsvp-latest.js"></script>
<script src="https://rawgithub.com/calvinmetcalf/catiline/master/dist/catiline.js"></script>

<script src="https://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 src="http://yui.yahooapis.com/3.14.1/build/yui/yui-min.js"></script>
<script>
YUI().use('promise', function (Y) {
    window.Y = Y;
});
</script>
<script src="http://s3.amazonaws.com/es6-promises/promise-0.1.1.min.js"></script>
<script>
window.kew = (function(){
/** @typedef {function(?, ?)} */
var OnSuccessCallbackType;
/** @typedef {function(!Error, ?)} */
var OnFailCallbackType;

/**
 * An object representing a "promise" for a future value
 *
 * @param {?OnSuccessCallbackType=} onSuccess a function to handle successful
 *     resolution of this promise
 * @param {OnFailCallbackType=} onFail a function to handle failed
 *     resolution of this promise
 * @constructor
 */
function Promise(onSuccess, onFail) {
  this.promise = this
  this._isPromise = true
  this._successFn = onSuccess
  this._failFn = onFail
  this._hasContext = false
  this._nextContext = undefined
  this._currentContext = undefined
}

/**
 * Specify that the current promise should have a specified context
 * @param  {*} context context
 * @private
 */
Promise.prototype._useContext = function (context) {
  this._nextContext = this._currentContext = context
  this._hasContext = true
  return this
}

Promise.prototype.clearContext = function () {
  this._hasContext = false
  this._nextContext = undefined
  return this
}

/**
 * Set the context for all promise handlers to follow
 * @param {*} context An arbitrary context
 */
Promise.prototype.setContext = function (context) {
  this._nextContext = context
  this._hasContext = true
  return this
}

/**
 * Get the context for a promise
 * @return {*} the context set by setContext
 */
Promise.prototype.getContext = function () {
  return this._nextContext
}

/**
 * Resolve this promise with a specified value
 *
 * @param {*} data
 */
Promise.prototype.resolve = function (data) {
  if (this._error || this._hasData) throw new Error("Unable to resolve or reject the same promise twice")

  var i
  if (data && isPromise(data)) {
    this._child = data
    if (this._promises) {
      for (i = 0; i < this._promises.length; i += 1) {
        data._chainPromise(this._promises[i])
      }
      delete this._promises
    }

    if (this._onComplete) {
      for (i = 0; i < this._onComplete.length; i+= 1) {
        data.fin(this._onComplete[i])
      }
      delete this._onComplete
    }
  } else if (data && isPromiseLike(data)) {
    data.then(
      function(data) { this.resolve(data) }.bind(this),
      function(err) { this.reject(err) }.bind(this)
    )
  } else {
    this._hasData = true
    this._data = data

    if (this._onComplete) {
      for (i = 0; i < this._onComplete.length; i++) {
        this._onComplete[i]()
      }
    }

    if (this._promises) {
      for (i = 0; i < this._promises.length; i += 1) {
        this._promises[i]._withInput(data)
      }
      delete this._promises
    }
  }
}

/**
 * Reject this promise with an error
 *
 * @param {!Error} e
 */
Promise.prototype.reject = function (e) {
  if (this._error || this._hasData) throw new Error("Unable to resolve or reject the same promise twice")

  var i
  this._error = e

  if (this._ended) {
    process.nextTick(function onPromiseThrow() {
      throw e
    })
  }

  if (this._onComplete) {
    for (i = 0; i < this._onComplete.length; i++) {
      this._onComplete[i]()
    }
  }

  if (this._promises) {
    for (i = 0; i < this._promises.length; i += 1) {
      this._promises[i]._withError(e)
    }
    delete this._promises
  }
}

/**
 * Provide a callback to be called whenever this promise successfully
 * resolves. Allows for an optional second callback to handle the failure
 * case.
 *
 * @param {?OnSuccessCallbackType} onSuccess
 * @param {OnFailCallbackType=} onFail
 * @return {!Promise} returns a new promise with the output of the onSuccess or
 *     onFail handler
 */
Promise.prototype.then = function (onSuccess, onFail) {
  var promise = new Promise(onSuccess, onFail)
  if (this._nextContext) promise._useContext(this._nextContext)

  if (this._child) this._child._chainPromise(promise)
  else this._chainPromise(promise)

  return promise
}

/**
 * Provide a callback to be called whenever this promise is rejected
 *
 * @param {OnFailCallbackType} onFail
 * @return {!Promise} returns a new promise with the output of the onFail handler
 */
Promise.prototype.fail = function (onFail) {
  return this.then(null, onFail)
}

/**
 * Provide a callback to be called whenever this promise is either resolved
 * or rejected.
 *
 * @param {function()} onComplete
 * @return {!Promise} returns the current promise
 */
Promise.prototype.fin = function (onComplete) {
  if (this._hasData || this._error) {
    onComplete()
    return this
  }

  if (this._child) {
    this._child.fin(onComplete)
  } else {
    if (!this._onComplete) this._onComplete = [onComplete]
    else this._onComplete.push(onComplete)
  }

  return this
}

/**
 * Mark this promise as "ended". If the promise is rejected, this will throw an
 * error in whatever scope it happens to be in
 *
 * @return {!Promise} returns the current promise
 */
Promise.prototype.end = function () {
  if (this._error) {
    throw this._error
  }
  this._ended = true
  return this
}

/**
 * Return a new promise that behaves the same as the current promise except
 * that it will be rejected if the current promise does not get fulfilled
 * after a certain amount of time.
 *
 * @param {number} timeoutMs The timeout threshold in msec
 * @param {string=} timeoutMsg error message
 * @returns a new promise with timeout
 */
 Promise.prototype.timeout = function (timeoutMs, timeoutMsg) {
  var deferred = new Promise()
  var isTimeout = false

  var timeout = setTimeout(function() {
    deferred.reject(new Error(timeoutMsg || 'Promise timeout after ' + timeoutMs + ' ms.'))
    isTimeout = true
  }, timeoutMs)

  this.then(function (data) {
    if (!isTimeout) {
      clearTimeout(timeout)
      deferred.resolve(data)
    }
  },
  function (err) {
    if (!isTimeout) {
      clearTimeout(timeout)
      deferred.reject(err)
    }
  })

  return deferred.promise
}

/**
 * Attempt to resolve this promise with the specified input
 *
 * @param {*} data the input
 */
Promise.prototype._withInput = function (data) {
  if (this._successFn) {
    try {
      this.resolve(this._successFn(data, this._currentContext))
    } catch (e) {
      this.reject(e)
    }
  } else this.resolve(data)

  // context is no longer needed
  delete this._currentContext
}

/**
 * Attempt to reject this promise with the specified error
 *
 * @param {!Error} e
 * @private
 */
Promise.prototype._withError = function (e) {
  if (this._failFn) {
    try {
      this.resolve(this._failFn(e, this._currentContext))
    } catch (thrown) {
      this.reject(thrown)
    }
  } else this.reject(e)

  // context is no longer needed
  delete this._currentContext
}

/**
 * Chain a promise to the current promise
 *
 * @param {!Promise} promise the promise to chain
 * @private
 */
Promise.prototype._chainPromise = function (promise) {
  var i
  if (this._hasContext) promise._useContext(this._nextContext)

  if (this._child) {
    this._child._chainPromise(promise)
  } else if (this._hasData) {
    promise._withInput(this._data)
  } else if (this._error) {
    promise._withError(this._error)
  } else if (!this._promises) {
    this._promises = [promise]
  } else {
    this._promises.push(promise)
  }
}

/**
 * Utility function used for creating a node-style resolver
 * for deferreds
 *
 * @param {!Promise} deferred a promise that looks like a deferred
 * @param {Error=} err an optional error
 * @param {*=} data optional data
 */
function resolver(deferred, err, data) {
  if (err) deferred.reject(err)
  else deferred.resolve(data)
}

/**
 * Creates a node-style resolver for a deferred by wrapping
 * resolver()
 *
 * @return {function(?Error, *)} node-style callback
 */
Promise.prototype.makeNodeResolver = function () {
  return resolver.bind(null, this)
}

/**
 * Return true iff the given object is a promise of this library.
 *
 * Because kew's API is slightly different than other promise libraries,
 * it's important that we have a test for its promise type. If you want
 * to test for a more general A+ promise, you should do a cap test for
 * the features you want.
 *
 * @param {*} obj The object to test
 * @return {boolean} Whether the object is a promise
 */
function isPromise(obj) {
  return !!obj._isPromise
}

/**
 * Return true iff the given object is a promise-like object, e.g. appears to
 * implement Promises/A+ specification
 *
 * @param {*} obj The object to test
 * @return {boolean} Whether the object is a promise-like object
 */
function isPromiseLike(obj) {
  return typeof obj === 'object' && typeof obj.then === 'function'
}

/**
 * Static function which creates and resolves a promise immediately
 *
 * @param {*} data data to resolve the promise with
 * @return {!Promise}
 */
function resolve(data) {
  var promise = new Promise()
  promise.resolve(data)
  return promise
}

/**
 * Static function which creates and rejects a promise immediately
 *
 * @param {!Error} e error to reject the promise with
 * @return {!Promise}
 */
function reject(e) {
  var promise = new Promise()
  promise.reject(e)
  return promise
}

/**
 * Replace an element in an array with a new value. Used by .all() to
 * call from .then()
 *
 * @param {!Array} arr
 * @param {number} idx
 * @param {*} val
 * @return {*} the val that's being injected into the array
 */
function replaceEl(arr, idx, val) {
  arr[idx] = val
  return val
}

/**
 * Replace an element in an array as it is resolved with its value.
 * Used by .allSettled().
 *
 * @param {!Array} arr
 * @param {number} idx
 * @param {*} value The value from a resolved promise.
 * @return {*} the data that's being passed in
 */
function replaceElFulfilled(arr, idx, value) {
  arr[idx] = {
    state: 'fulfilled',
    value: value
  }
  return value
}

/**
 * Replace an element in an array as it is rejected with the reason.
 * Used by .allSettled().
 *
 * @param {!Array} arr
 * @param {number} idx
 * @param {*} reason The reason why the original promise is rejected
 * @return {*} the data that's being passed in
 */
function replaceElRejected(arr, idx, reason) {
  arr[idx] = {
    state: 'rejected',
    reason: reason
  }
  return reason
}

/**
 * Takes in an array of promises or literals and returns a promise which returns
 * an array of values when all have resolved. If any fail, the promise fails.
 *
 * @param {!Array.<!Promise>} promises
 * @return {!Promise}
 */
function all(promises) {
  if (arguments.length != 1 || !Array.isArray(promises)) {
    promises = Array.prototype.slice.call(arguments, 0)
  }
  if (!promises.length) return resolve([])

  var outputs = []
  var finished = false
  var promise = new Promise()
  var counter = promises.length

  for (var i = 0; i < promises.length; i += 1) {
    if (!promises[i] || !isPromiseLike(promises[i])) {
      outputs[i] = promises[i]
      counter -= 1
    } else {
      promises[i].then(replaceEl.bind(null, outputs, i))
      .then(function decrementAllCounter() {
        counter--
        if (!finished && counter === 0) {
          finished = true
          promise.resolve(outputs)
        }
      }, function onAllError(e) {
        if (!finished) {
          finished = true
          promise.reject(e)
        }
      })
    }
  }

  if (counter === 0 && !finished) {
    finished = true
    promise.resolve(outputs)
  }

  return promise
}

/**
 * Takes in an array of promises or literals and returns a promise which returns
 * an array of values when all have resolved or rejected.
 *
 * @param {!Array.<!Promise>} promises
 * @return {!Array.<Object>} The state of the promises. If a promise is resolved,
 *     its corresponding state object is {state: 'fulfilled', value: Object};
 *     whereas if a promise is rejected, its corresponding state object is
 *     {state: 'rejected', reason: Object}
 */
function allSettled(promises) {
  if (!Array.isArray(promises)) {
    throw Error('The input to "allSettled()" should be an array of Promise')
  }
  if (!promises.length) return resolve([])

  var outputs = []
  var promise = new Promise()
  var counter = promises.length

  for (var i = 0; i < promises.length; i += 1) {
    if (!promises[i] || !isPromiseLike(promises[i])) {
      replaceElFulfilled(outputs, i, promises[i])
      if ((--counter) === 0) promise.resolve(outputs)
    } else {
      promises[i]
        .then(replaceElFulfilled.bind(null, outputs, i), replaceElRejected.bind(null, outputs, i))
        .then(function () {
          if ((--counter) === 0) promise.resolve(outputs)
        })
    }
  }

  return promise
}

/**
 * Create a new Promise which looks like a deferred
 *
 * @return {!Promise}
 */
function defer() {
  return new Promise()
}

/**
 * Return a promise which will wait a specified number of ms to resolve
 *
 * @param {number} delayMs
 * @param {*} returnVal
 * @return {!Promise} returns returnVal
 */
function delay(delayMs, returnVal) {
  var defer = new Promise()
  setTimeout(function onDelay() {
    defer.resolve(returnVal)
  }, delayMs)
  return defer
}

/**
 * Return a promise which will evaluate the function fn in a future turn with
 * the provided args
 *
 * @param {function(...)} fn
 * @param {...} var_args a variable number of arguments
 * @return {!Promise}
 */
function fcall(fn, var_args) {
  var rootArgs = Array.prototype.slice.call(arguments, 1)
  var defer = new Promise()
  process.nextTick(function onNextTick() {
    defer.resolve(fn.apply(undefined, rootArgs))
  })
  return defer
}


/**
 * Returns a promise that will be invoked with the result of a node style
 * callback. All args to fn should be given except for the final callback arg
 *
 * @param {function(...)} fn
 * @param {...} var_args a variable number of arguments
 * @return {!Promise}
 */
function nfcall(fn, var_args) {
  // Insert an undefined argument for scope and let bindPromise() do the work.
  var args = Array.prototype.slice.call(arguments, 0)
  args.splice(1, 0, undefined)
  return bindPromise.apply(undefined, args)()
}


/**
 * Binds a function to a scope with an optional number of curried arguments. Attaches
 * a node style callback as the last argument and returns a promise
 *
 * @param {function(...)} fn
 * @param {Object} scope
 * @param {...} var_args a variable number of arguments
 * @return {function(...)}: !Promise}
 */
function bindPromise(fn, scope, var_args) {
  var rootArgs = Array.prototype.slice.call(arguments, 2)
  return function onBoundPromise(var_args) {
    var defer = new Promise()
    fn.apply(scope, rootArgs.concat(Array.prototype.slice.call(arguments, 0), defer.makeNodeResolver()))
    return defer
  }
}
var module = {};
module.exports = {
    all: all
  , bindPromise: bindPromise
  , defer: defer
  , delay: delay
  , fcall: fcall
  , isPromise: isPromise
  , isPromiseLike: isPromiseLike
  , nfcall: nfcall
  , resolve: resolve
  , reject: reject
  , allSettled: allSettled
}
return module.exports;
}())




/**
 * @author RubaXa <trash@rubaxa.org>
 * @license MIT
 */
;(function (){


        function _then(promise, method, callback){
                return function (){
                        var args = arguments;

                        if( typeof callback === 'function' ){
                                var retVal = callback.apply(promise, args);
                                if( retVal && typeof retVal.then === 'function' ){
                                        retVal.done(promise.resolve).fail(promise.reject);
                                        return;
                                }
                        }

                        promise[method].apply(promise, args);
                };
        }



        /**
         * Fastest Deferred.
         * @returns {Deferred}
         */
        var Deferred = function (){
                var
                        _args,
                        _doneFn = [],
                        _failFn = [],

                        dfd = {
                                done: function (fn){
                                        _doneFn.push(fn);
                                        return dfd;
                                },

                                fail: function (fn){
                                        _failFn.push(fn);
                                        return dfd;
                                },

                                then: function (doneFn, failFn){
                                        var promise = Deferred();

                                        dfd
                                                .done(_then(promise, 'resolve', doneFn))
                                                .fail(_then(promise, 'reject', failFn))
                                        ;

                                        return promise;
                                },

                                always: function (fn){
                                        return dfd.done(fn).fail(fn);
                                },

                                resolve: _setState(true),
                                reject: _setState(false)
                        }
                ;


                function _setState(state){
                        return function (){
                                _args = arguments;

                                dfd.done =
                                dfd.fail =
                                dfd.resolve =
                                dfd.reject = function (){
                                        return dfd;
                                };

                                dfd[state ? 'done' : 'fail'] = function (fn){
                                        if( typeof fn === 'function' ){
                                                fn.apply(dfd, _args);
                                        }
                                        return dfd;
                                };

                                var
                                          fn
                                        , fns = state ? _doneFn : _failFn
                                        , i = 0, n = fns.length
                                ;

                                for( ; i < n; i++ ){
                                        fn = fns[i];
                                        if( typeof fn === 'function' ){
                                                fn.apply(dfd, _args);
                                        }
                                }

                                fns = _doneFn = _failFn = null;

                                return dfd;
                        }
                }

                return dfd;
        };


        /**
         * @param {Array} args
         * @returns {defer|*}
         */
        Deferred.when = function (args){
                var
                          dfd = Deferred()
                        , d
                        , i = args.length
                        , remain = i || 1
                        , _doneFn = function (){
                                if( --remain === 0 ){
                                        dfd.resolve();
                                }
                        }
                ;

                if( i === 0 ){
                        _doneFn();
                }
                else {
                        while( i-- ){
                                d = args[i];
                                if( d && d.then ){
                                        d.then(_doneFn, dfd.reject);
                                }
                        }
                }

                return dfd;
        };


        // exports
  window.MyDeferred = Deferred;
})();
window.MyPromise = function () {
  var global = Function('return this')();
  var isObject = function (arg) {
        return typeof arg === 'object' &&
            arg !== null;
    };
  var isFunction = function (arg) {
        return typeof arg === 'function';
    };
  var isObjectOrFunction = function (arg) {
        return isObject(arg) || isFunction(arg);
    };
  var isNative = function (target) {
        return typeof target === 'object' ||
            (target + '').slice(-17) === '{ [native code] }';
    };
  var final_ = function (initialiser, value) {
        return function () {
            if (initialiser !== null) {
                value = initialiser(value);
                initialiser = null;
            }
            return value;
        };
    };
  var scheduleMicrotask = function () {

    var finalSchedule = function () {
        var queue = [];

        function flushQueue() {
            var tasks = queue;
            queue = [];
            for (var i = 0; i < tasks.length; i++) {
                tasks[i]();
            }
        }

        function usePromise() {
            return null;
            if (!isNative(global.Promise)) {
                return null;
            }
            var fulfilled = new Promise(function (resolve) {
                resolve();
            });
            return function () {
                fulfilled.then(flushQueue);
            };
        }

        function useMutationObserver() {
            if (!isNative(global.MutationObserver)) {
                return null;
            }
            var iterations = 0;
            var observer = new MutationObserver(flushQueue);
            var node = document.createTextNode('');
            observer.observe(node, { characterData: true });
            return function() {
                node.data = (iterations = ++iterations % 2);
            };
        }

        function useSetImmediate() {
            if (!isNative(global.setImmediate)) {
                return null;
            }

            var immediateId;
            var timerId;
            function raceFlush() {
                clearTimeout(timerId);
                clearImmediate(immediateId);
                flushQueue();
            }
            return function () {
                immediateId = setImmediate(raceFlush);
                timerId = setTimeout(raceFlush);
            };
        }

        function useSetTimeout() {
            return function () {
                setTimeout(flushQueue);
            };
        }

        var scheduleFlush = usePromise() ||
            useMutationObserver() ||
            useSetImmediate() ||
            useSetTimeout();

        return function (callback) {
            queue.push(callback);
            if (queue.length <= 1) {
                scheduleFlush();
            }
        };
    }();

    return function (callback) {
        finalSchedule(callback);
    };
  }();
  return function () {
        var PENDING = 0;
        var FULFILLED = 1;
        var REJECTED = 2;

        function Promise(resolver) {
           this._state = PENDING;
           this._value = [];
            var promise = this;
            try {
                resolver(function (value) {
                    resolve(promise, value);
                }, function (reason) {
                    reject(promise, reason);
                });
            } catch (ex) {
                reject(promise, ex);
            }
        }

        function Promise2() {
           this._state = PENDING;
           this._value = [];
        }
        Promise2.prototype = Promise.prototype;

        Promise.prototype._state = PENDING;
        Promise.prototype._value = '';

        function invokeCallback(settled, value, callback, promise) {
            if (isFunction(callback)) {
                try {
                    resolve(promise, callback(value));
                } catch (ex) {
                    reject(promise, ex);
                }
            } else {
                publish(promise, settled, value);
            }
        }

        function publish(promise, settled, value) {
            var subscribers = promise._value;
            promise._state = settled;
            promise._value = value;
            for (var i = 0; i < subscribers.length; i += 3) {
                invokeCallback(
                    settled,
                    value,
                    subscribers[i + settled],
                    subscribers[i]);
            }
        }

        function schedulePublish(promise, state, value) {
            scheduleMicrotask(function () {
                publish(promise, state, value);
            });
        }

        function isThenable(value) {
            return isObjectOrFunction(value) &&
                isFunction(value.then);
        }

        function chainThenable(thenable, promise) {
            try {
                thenable.then(function (value) {
                    resolve(promise, value);
                }, function (reason) {
                    reject(promise, reason);
                });
            } catch (ex) {
                reject(promise, ex);
            }
        }

        function resolve(promise, value) {
            if (value !== this && isThenable(value)) {
                chainThenable(value, promise);
            } else {
                schedulePublish(promise, FULFILLED, value);
            }
        }

        function reject(promise, reason) {
            schedulePublish(promise, REJECTED, reason);
        }

        Promise.prototype.then = function (onFulfilled, onRejected) {
            var childPromise = new Promise2();
            if (this._state <= PENDING) {
                
                    this._value.push(childPromise);
                    this._value.push(onFulfilled);
                    this._value.push(onRejected);
            } else {
                var callback = (this._state >= REJECTED) ?
                    onRejected : onFulfilled;
                if (!isFunction(callback)) {
                    publish(childPromise, this._state, this._value);
                } else {
                    var promise = this;
                    scheduleMicrotask(function () {
                        invokeCallback(
                            promise._state,
                            promise._value,
                            callback,
                            childPromise);
                    });
                }
            }
            return childPromise;
        };
        window.MyPromise2 = Promise2;
        window.resolveMyPromise = resolve;
        return Promise;
    }();
  }();
</script>

Setup

/**
     * @module vow
     * @author Filatov Dmitry <dfilatov@yandex-team.ru>
     * @version 0.4.8
     * @license
     * Dual licensed under the MIT and GPL licenses:
     *   * http://www.opensource.org/licenses/mit-license.php
     *   * http://www.gnu.org/licenses/gpl.html
     */
    (function(e) {
      var t, n = function() {
          var t = [],
            n = function(e) {
              return t.push(e) === 1
            }, r = function() {
              var e = t,
                n = 0,
                r = t.length;
              t = [];
              while (n < r) e[n++]()
            };
          if (typeof setImmediate == "function") return function(e) {
            n(e) && setImmediate(r)
          };
          if (typeof process == "object" && process.nextTick) return function(e) {
            n(e) && process.nextTick(r)
          };
          if (e.postMessage) {
            var i = !0;
            if (e.attachEvent) {
              var s = function() {
                i = !1
              };
              e.attachEvent("onmessage", s), e.postMessage("__checkAsync", "*"), e.detachEvent("onmessage", s)
            }
            if (i) {
              var o = "__promise" + +(new Date),
                u = function(e) {
                  e.data === o && (e.stopPropagation && e.stopPropagation(), r())
                };
              return e.addEventListener ? e.addEventListener("message", u, !0) : e.attachEvent("onmessage", u),
              function(t) {
                n(t) && e.postMessage(o, "*")
              }
            }
          }
          var a = e.document;
          if ("onreadystatechange" in a.createElement("script")) {
            var f = function() {
              var e = a.createElement("script");
              e.onreadystatechange = function() {
                e.parentNode.removeChild(e), e = e.onreadystatechange = null, r()
              }, (a.documentElement || a.body).appendChild(e)
            };
            return function(e) {
              n(e) && f()
            }
          }
          return function(e) {
            n(e) && setTimeout(r, 0)
          }
        }(),
        r = function(e) {
          n(function() {
            throw e
          })
        }, i = function(e) {
          return typeof e == "function"
        }, s = function(e) {
          return e !== null && typeof e == "object"
        }, o = Object.prototype.toString,
        u = Array.isArray || function(e) {
          return o.call(e) === "[object Array]"
        }, a = function(e) {
          var t = [],
            n = 0,
            r = e.length;
          while (n < r) t.push(n++);
          return t
        }, f = Object.keys || function(e) {
          var t = [];
          for (var n in e) e.hasOwnProperty(n) && t.push(n);
          return t
        }, l = function(e) {
          var t = function(t) {
            this.name = e, this.message = t
          };
          return t.prototype = new Error, t
        }, c = function(e, t) {
          return function(n) {
            e.call(this, n, t)
          }
        }, h = function() {
          this._promise = new d
        };
      h.prototype = {
        promise: function() {
          return this._promise
        },
        resolve: function(e) {
          this._promise.isResolved() || this._promise._resolve(e)
        },
        reject: function(e) {
          if (this._promise.isResolved()) return;
          g.isPromise(e) ? (e = e.then(function(e) {
            var t = g.defer();
            return t.reject(e), t.promise()
          }), this._promise._resolve(e)) : this._promise._reject(e)
        },
        notify: function(e) {
          this._promise.isResolved() || this._promise._notify(e)
        }
      };
      var p = {
        PENDING: 0,
        RESOLVED: 1,
        FULFILLED: 2,
        REJECTED: 3
      }, d = function(e) {
          this._value = t, this._status = p.PENDING, this._fulfilledCallbacks = [], this._rejectedCallbacks = [], this._progressCallbacks = [];
          if (e) {
            var n = this,
              r = e.length;
            e(function(e) {
              n.isResolved() || n._resolve(e)
            }, r > 1 ? function(e) {
              n.isResolved() || n._reject(e)
            } : t, r > 2 ? function(e) {
              n.isResolved() || n._notify(e)
            } : t)
          }
        };
      d.prototype = {
        valueOf: function() {
          return this._value
        },
        isResolved: function() {
          return this._status !== p.PENDING
        },
        isFulfilled: function() {
          return this._status === p.FULFILLED
        },
        isRejected: function() {
          return this._status === p.REJECTED
        },
        then: function(e, t, n, r) {
          var i = new h;
          return this._addCallbacks(i, e, t, n, r), i.promise()
        },
        "catch": function(e, n) {
          return this.then(t, e, n)
        },
        fail: function(e, n) {
          return this.then(t, e, n)
        },
        always: function(e, t) {
          var n = this,
            r = function() {
              return e.call(this, n)
            };
          return this.then(r, r, t)
        },
        progress: function(e, n) {
          return this.then(t, t, e, n)
        },
        spread: function(e, t, n) {
          return this.then(function(t) {
            return e.apply(this, t)
          }, t, n)
        },
        done: function(e, t, n, i) {
          this.then(e, t, n, i).fail(r)
        },
        delay: function(e) {
          var t, n = this.then(function(n) {
              var r = new h;
              return t = setTimeout(function() {
                r.resolve(n)
              }, e), r.promise()
            });
          return n.always(function() {
            clearTimeout(t)
          }), n
        },
        timeout: function(e) {
          var t = new h,
            n = setTimeout(function() {
              t.reject(new g.TimedOutError("timed out"))
            }, e);
          return this.then(function(e) {
            t.resolve(e)
          }, function(e) {
            t.reject(e)
          }), t.promise().always(function() {
            clearTimeout(n)
          }), t.promise()
        },
        _vow: !0,
        _resolve: function(e) {
          if (this._status > p.RESOLVED) return;
          if (e === this) {
            this._reject(TypeError("Can't resolve promise with itself"));
            return
          }
          this._status = p.RESOLVED;
          if (e && !! e._vow) {
            e.isFulfilled() ? this._fulfill(e.valueOf()) : e.isRejected() ? this._reject(e.valueOf()) : e.then(this._fulfill, this._reject, this._notify, this);
            return
          }
          if (s(e) || i(e)) {
            var t;
            try {
              t = e.then
            } catch (n) {
              this._reject(n);
              return
            }
            if (i(t)) {
              var r = this,
                o = !1;
              try {
                t.call(e, function(e) {
                  if (o) return;
                  o = !0, r._resolve(e)
                }, function(e) {
                  if (o) return;
                  o = !0, r._reject(e)
                }, function(e) {
                  r._notify(e)
                })
              } catch (n) {
                o || this._reject(n)
              }
              return
            }
          }
          this._fulfill(e)
        },
        _fulfill: function(e) {
          if (this._status > p.RESOLVED) return;
          this._status = p.FULFILLED, this._value = e, this._callCallbacks(this._fulfilledCallbacks, e), this._fulfilledCallbacks = this._rejectedCallbacks = this._progressCallbacks = t
        },
        _reject: function(e) {
          if (this._status > p.RESOLVED) return;
          this._status = p.REJECTED, this._value = e, this._callCallbacks(this._rejectedCallbacks, e), this._fulfilledCallbacks = this._rejectedCallbacks = this._progressCallbacks = t
        },
        _notify: function(e) {
          this._callCallbacks(this._progressCallbacks, e)
        },
        _addCallbacks: function(e, n, r, s, o) {
          r && !i(r) ? (o = r, r = t) : s && !i(s) && (o = s, s = t);
          var u;
          this.isRejected() || (u = {
            defer: e,
            fn: i(n) ? n : t,
            ctx: o
          }, this.isFulfilled() ? this._callCallbacks([u], this._value) : this._fulfilledCallbacks.push(u)), this.isFulfilled() || (u = {
            defer: e,
            fn: r,
            ctx: o
          }, this.isRejected() ? this._callCallbacks([u], this._value) : this._rejectedCallbacks.push(u)), this._status <= p.RESOLVED && this._progressCallbacks.push({
            defer: e,
            fn: s,
            ctx: o
          })
        },
        _callCallbacks: function(e, t) {
          var r = e.length;
          if (!r) return;
          var i = this.isResolved(),
            s = this.isFulfilled();
          n(function() {
            var n = 0,
              o, u, a;
            while (n < r) {
              o = e[n++], u = o.defer, a = o.fn;
              if (a) {
                var f = o.ctx,
                  l;
                try {
                  l = f ? a.call(f, t) : a(t)
                } catch (c) {
                  u.reject(c);
                  continue
                }
                i ? u.resolve(l) : u.notify(l)
              } else i ? s ? u.resolve(t) : u.reject(t) : u.notify(t)
            }
          })
        }
      };
      var v = {
        cast: function(e) {
          return g.cast(e)
        },
        all: function(e) {
          return g.all(e)
        },
        race: function(e) {
          return g.anyResolved(e)
        },
        resolve: function(e) {
          return g.resolve(e)
        },
        reject: function(e) {
          return g.reject(e)
        }
      };
      for (var m in v) v.hasOwnProperty(m) && (d[m] = v[m]);
      var g = {
        Deferred: h,
        Promise: d,
        defer: function() {
          return new h
        },
        when: function(e, t, n, r, i) {
          return g.cast(e).then(t, n, r, i)
        },
        fail: function(e, n, r) {
          return g.when(e, t, n, r)
        },
        always: function(e, t, n) {
          return g.when(e).always(t, n)
        },
        progress: function(e, t, n) {
          return g.when(e).progress(t, n)
        },
        spread: function(e, t, n, r) {
          return g.when(e).spread(t, n, r)
        },
        done: function(e, t, n, r, i) {
          g.when(e).done(t, n, r, i)
        },
        isPromise: function(e) {
          return s(e) && i(e.then)
        },
        cast: function(e) {
          return g.isPromise(e) ? e : g.resolve(e)
        },
        valueOf: function(e) {
          return e && i(e.valueOf) ? e.valueOf() : e
        },
        isFulfilled: function(e) {
          return e && i(e.isFulfilled) ? e.isFulfilled() : !0
        },
        isRejected: function(e) {
          return e && i(e.isRejected) ? e.isRejected() : !1
        },
        isResolved: function(e) {
          return e && i(e.isResolved) ? e.isResolved() : !0
        },
        resolve: function(e) {
          var t = g.defer();
          return t.resolve(e), t.promise()
        },
        fulfill: function(e) {
          var t = g.defer(),
            n = t.promise();
          return t.resolve(e), n.isFulfilled() ? n : n.then(null, function(e) {
            return e
          })
        },
        reject: function(e) {
          var t = g.defer();
          return t.reject(e), t.promise()
        },
        invoke: function(t, n) {
          var r = Math.max(arguments.length - 1, 0),
            i;
          if (r) {
            i = Array(r);
            var s = 0;
            while (s < r) i[s++] = arguments[s]
          }
          try {
            return g.resolve(i ? t.apply(e, i) : t.call(e))
          } catch (o) {
            return g.reject(o)
          }
        },
        all: function(e) {
          var t = new h,
            n = u(e),
            r = n ? a(e) : f(e),
            i = r.length,
            s = n ? [] : {};
          if (!i) return t.resolve(s), t.promise();
          var o = i;
          return g._forEach(e, function(e, n) {
            s[r[n]] = e, --o || t.resolve(s)
          }, t.reject, t.notify, t, r), t.promise()
        },
        allResolved: function(e) {
          var t = new h,
            n = u(e),
            r = n ? a(e) : f(e),
            i = r.length,
            s = n ? [] : {};
          if (!i) return t.resolve(s), t.promise();
          var o = function() {
            --i || t.resolve(e)
          };
          return g._forEach(e, o, o, t.notify, t, r), t.promise()
        },
        allPatiently: function(e) {
          return g.allResolved(e).then(function() {
            var t = u(e),
              n = t ? a(e) : f(e),
              r, i, s = n.length,
              o = 0,
              l, c;
            if (!s) return t ? [] : {};
            while (o < s) l = n[o++], c = e[l], g.isRejected(c) ? (r || (r = t ? [] : {}), t ? r.push(c.valueOf()) : r[l] = c.valueOf()) : r || ((i || (i = t ? [] : {}))[l] = g.valueOf(c));
            if (r) throw r;
            return i
          })
        },
        any: function(e) {
          var t = new h,
            n = e.length;
          if (!n) return t.reject(Error()), t.promise();
          var r = 0,
            i;
          return g._forEach(e, t.resolve, function(e) {
            r || (i = e), ++r === n && t.reject(i)
          }, t.notify, t), t.promise()
        },
        anyResolved: function(e) {
          var t = new h,
            n = e.length;
          return n ? (g._forEach(e, t.resolve, t.reject, t.notify, t), t.promise()) : (t.reject(Error()), t.promise())
        },
        delay: function(e, t) {
          return g.resolve(e).delay(t)
        },
        timeout: function(e, t) {
          return g.resolve(e).timeout(t)
        },
        _forEach: function(e, t, n, r, i, s) {
          var o = s ? s.length : e.length,
            u = 0;
          while (u < o) g.when(e[s ? s[u] : u], c(t, u), n, r, i), ++u
        },
        TimedOutError: l("TimedOut")
      }, y = !0;
      typeof module == "object" && typeof module.exports == "object" && (module.exports = g, y = !1), typeof modules == "object" && i(modules.define) && (modules.define("vow", function(e) {
        e(g)
      }), y = !1), typeof define == "function" && (define(function(e, t, n) {
        n.exports = g
      }), y = !1), y && (e.vow = g)
    })(this);
    
    function doSomethingAsync() {
      var deferred = vow.defer();
    
      // now you can resolve, reject, notify corresponging promise within `deferred`
      // e.g. `defer.resolve('ok');`
      deferred.resolve();
      return deferred.promise(); // and return corresponding promise to subscribe to reactions
    }

Test runner

Ready to run.

Testing in
TestOps/sec
lie
// async test
var eventFunc;
var code = 'lie' + Math.random();
var d = lie(function(resolve) {

  eventFunc = function(e) {
    if (e === code) {
      worker.off('pong');
      resolve();
    }
  }
});
worker.on('pong', eventFunc);
d.then(function() {
  deferred.resolve()
});
worker.fire('ping', code);
ready
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);
ready
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);
ready
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);
ready
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);
ready
bluebird
// async test
var eventFunc;
var code = 'bluebird' + Math.random();
var d = BluebirdPromise.pending()


eventFunc = function(e) {
  if (e === code) {
    worker.off('pong');
    d.fulfill();
  }
};

worker.on('pong', eventFunc);
d.promise.then(function() {
  deferred.resolve();
});
worker.fire('ping', code);
ready
yui
// async test
var eventFunc;
var code = 'yui' + Math.random();
var d = new Y.Promise(function(resolve) {

  eventFunc = function(e) {
    if (e === code) {
      worker.off('pong');
      resolve();
    }
  }
});
worker.on('pong', eventFunc);
d.then(function() {
  deferred.resolve();
});
worker.fire('ping', code);
ready
JQuery
// async test
var eventFunc;
var code = 'jquery' + Math.random();
var d = new jQuery.Deferred();


eventFunc = function(e) {
  if (e === code) {
    worker.off('pong');
    d.resolve();
  }
}

worker.on('pong', eventFunc);
d.then(function() {
  deferred.resolve();
});
worker.fire('ping', code);
ready
ES6 Promise polyfill
// async test
var eventFunc;
var code = 'es6' + Math.random();
var d = new Promise(function(resolve) {

  eventFunc = function(e) {
    if (e === code) {
      worker.off('pong');
      resolve();
    }
  }
});

worker.on('pong', eventFunc);

d.then(function() {
  deferred.resolve();
});

worker.fire('ping', code);
ready
YUI + ES6 Promise polyfill
// async test
var eventFunc;
var code = 'yui+es6' + Math.random();
var d = Promise.cast(new Y.Promise(function(resolve) {

  eventFunc = function(e) {
    if (e === code) {
      worker.off('pong');
      resolve();
    }
  }
}));
worker.on('pong', eventFunc);
d.then(function() {
  deferred.resolve();
});
worker.fire('ping', code);
ready
kew
// async test
var d = kew.defer()
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);
ready
MyDeferred
// async test
var eventFunc;
var code = 'myDeferred' + Math.random();
var d = MyDeferred();


eventFunc = function(e) {
  if (e === code) {
    worker.off('pong');
    d.resolve();
  }
}

worker.on('pong', eventFunc);
d.then(function() {
  deferred.resolve();
});
worker.fire('ping', code);
ready
MyPromise
// async test
var d = new MyPromise2();
var code = 'x' + Math.random();

function eventFunc(e) {
  if (e === code) {
    worker.off('pong');
    resolveMyPromise(d);
  }
}
worker.on('pong', eventFunc);
d.then(function() {
  deferred.resolve();
})
worker.fire('ping', code);
ready
Orion
// async test
var d = new orion.Deferred();
var code = 'orion' + Math.random();

function eventFunc(e) {
  if (e === code) {
    worker.off('pong');
    d.resolve();
  }
}
worker.on('pong', eventFunc);
d.then(function() {
  deferred.resolve();
})
worker.fire('ping', code);
ready
vow
// async test
doSomethingAsync();
ready

Revisions

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