Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /** @license MIT License (c) copyright 2011-2013 original author or authors */
- /**
- * A lightweight CommonJS Promises/A and when() implementation
- * when is part of the cujo.js family of libraries (http://cujojs.com/)
- *
- * Licensed under the MIT License at:
- * http://www.opensource.org/licenses/mit-license.php
- *
- * @author Brian Cavalier
- * @author John Hann
- * @version 2.2.1
- */
- // MPJ Removed AMD define() code - using simplified code instead
- window.when221 = (function() {
- var global = window;
- // Public API
- when.promise = promise; // Create a pending promise
- when.resolve = resolve; // Create a resolved promise
- when.reject = reject; // Create a rejected promise
- when.defer = defer; // Create a {promise, resolver} pair
- when.join = join; // Join 2 or more promises
- when.all = all; // Resolve a list of promises
- when.map = map; // Array.map() for promises
- when.reduce = reduce; // Array.reduce() for promises
- when.settle = settle; // Settle a list of promises
- when.any = any; // One-winner race
- when.some = some; // Multi-winner race
- when.isPromise = isPromise; // Determine if a thing is a promise
- /**
- * Register an observer for a promise or immediate value.
- *
- * @param {*} promiseOrValue
- * @param {function?} [onFulfilled] callback to be called when promiseOrValue is
- * successfully fulfilled. If promiseOrValue is an immediate value, callback
- * will be invoked immediately.
- * @param {function?} [onRejected] callback to be called when promiseOrValue is
- * rejected.
- * @param {function?} [onProgress] callback to be called when progress updates
- * are issued for promiseOrValue.
- * @returns {Promise} a new {@link Promise} that will complete with the return
- * value of callback or errback or the completion value of promiseOrValue if
- * callback and/or errback is not supplied.
- */
- function when(promiseOrValue, onFulfilled, onRejected, onProgress) {
- // Get a trusted promise for the input promiseOrValue, and then
- // register promise handlers
- return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress);
- }
- /**
- * Trusted Promise constructor. A Promise created from this constructor is
- * a trusted when.js promise. Any other duck-typed promise is considered
- * untrusted.
- * @constructor
- * @name Promise
- */
- function Promise(then, inspect) {
- this.then = then;
- this.inspect = inspect;
- }
- Promise.prototype = {
- /**
- * Register a rejection handler. Shortcut for .then(undefined, onRejected)
- * @param {function?} onRejected
- * @return {Promise}
- */
- otherwise: function(onRejected) {
- return this.then(undef, onRejected);
- },
- /**
- * Ensures that onFulfilledOrRejected will be called regardless of whether
- * this promise is fulfilled or rejected. onFulfilledOrRejected WILL NOT
- * receive the promises' value or reason. Any returned value will be disregarded.
- * onFulfilledOrRejected may throw or return a rejected promise to signal
- * an additional error.
- * @param {function} onFulfilledOrRejected handler to be called regardless of
- * fulfillment or rejection
- * @returns {Promise}
- */
- ensure: function(onFulfilledOrRejected) {
- return this.then(injectHandler, injectHandler)['yield'](this);
- function injectHandler() {
- return resolve(onFulfilledOrRejected());
- }
- },
- /**
- * Shortcut for .then(function() { return value; })
- * @param {*} value
- * @return {Promise} a promise that:
- * - is fulfilled if value is not a promise, or
- * - if value is a promise, will fulfill with its value, or reject
- * with its reason.
- */
- 'yield': function(value) {
- return this.then(function() {
- return value;
- });
- },
- /**
- * Assumes that this promise will fulfill with an array, and arranges
- * for the onFulfilled to be called with the array as its argument list
- * i.e. onFulfilled.apply(undefined, array).
- * @param {function} onFulfilled function to receive spread arguments
- * @return {Promise}
- */
- spread: function(onFulfilled) {
- return this.then(function(array) {
- // array may contain promises, so resolve its contents.
- return all(array, function(array) {
- return onFulfilled.apply(undef, array);
- });
- });
- },
- /**
- * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected)
- * @deprecated
- */
- always: function(onFulfilledOrRejected, onProgress) {
- return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress);
- }
- };
- /**
- * Returns a resolved promise. The returned promise will be
- * - fulfilled with promiseOrValue if it is a value, or
- * - if promiseOrValue is a promise
- * - fulfilled with promiseOrValue's value after it is fulfilled
- * - rejected with promiseOrValue's reason after it is rejected
- * @param {*} value
- * @return {Promise}
- */
- function resolve(value) {
- return promise(function(resolve) {
- resolve(value);
- });
- }
- /**
- * Returns a rejected promise for the supplied promiseOrValue. The returned
- * promise will be rejected with:
- * - promiseOrValue, if it is a value, or
- * - if promiseOrValue is a promise
- * - promiseOrValue's value after it is fulfilled
- * - promiseOrValue's reason after it is rejected
- * @param {*} promiseOrValue the rejected value of the returned {@link Promise}
- * @return {Promise} rejected {@link Promise}
- */
- function reject(promiseOrValue) {
- return when(promiseOrValue, rejected);
- }
- /**
- * Creates a {promise, resolver} pair, either or both of which
- * may be given out safely to consumers.
- * The resolver has resolve, reject, and progress. The promise
- * has then plus extended promise API.
- *
- * @return {{
- * promise: Promise,
- * resolve: function:Promise,
- * reject: function:Promise,
- * notify: function:Promise
- * resolver: {
- * resolve: function:Promise,
- * reject: function:Promise,
- * notify: function:Promise
- * }}}
- */
- function defer() {
- var deferred, pending, resolved;
- // Optimize object shape
- deferred = {
- promise: undef, resolve: undef, reject: undef, notify: undef,
- resolver: { resolve: undef, reject: undef, notify: undef }
- };
- deferred.promise = pending = promise(makeDeferred);
- return deferred;
- function makeDeferred(resolvePending, rejectPending, notifyPending) {
- deferred.resolve = deferred.resolver.resolve = function(value) {
- if(resolved) {
- return resolve(value);
- }
- resolved = true;
- resolvePending(value);
- return pending;
- };
- deferred.reject = deferred.resolver.reject = function(reason) {
- if(resolved) {
- return resolve(rejected(reason));
- }
- resolved = true;
- rejectPending(reason);
- return pending;
- };
- deferred.notify = deferred.resolver.notify = function(update) {
- notifyPending(update);
- return update;
- };
- }
- }
- /**
- * Creates a new promise whose fate is determined by resolver.
- * @param {function} resolver function(resolve, reject, notify)
- * @returns {Promise} promise whose fate is determine by resolver
- */
- function promise(resolver) {
- return _promise(resolver, monitorApi.PromiseStatus && monitorApi.PromiseStatus());
- }
- /**
- * Creates a new promise, linked to parent, whose fate is determined
- * by resolver.
- * @param {function} resolver function(resolve, reject, notify)
- * @param {Promise?} status promise from which the new promise is begotten
- * @returns {Promise} promise whose fate is determine by resolver
- * @private
- */
- function _promise(resolver, status) {
- var self, value, handlers = [];
- self = new Promise(then, inspect);
- // Call the provider resolver to seal the promise's fate
- try {
- resolver(promiseResolve, promiseReject, promiseNotify);
- } catch(e) {
- promiseReject(e);
- }
- // Return the promise
- return self;
- /**
- * Register handlers for this promise.
- * @param [onFulfilled] {Function} fulfillment handler
- * @param [onRejected] {Function} rejection handler
- * @param [onProgress] {Function} progress handler
- * @return {Promise} new Promise
- */
- function then(onFulfilled, onRejected, onProgress) {
- var next = _promise(function(resolve, reject, notify) {
- // if not resolved, push onto handlers, otherwise execute asap
- // but not in the current stack
- handlers ? handlers.push(run) : enqueue(function() { run(value); });
- function run(p) {
- p.then(onFulfilled, onRejected, onProgress)
- .then(resolve, reject, notify);
- }
- }, status && status.observed());
- return next;
- }
- function inspect() {
- return value ? value.inspect() : toPendingState();
- }
- /**
- * Transition from pre-resolution state to post-resolution state, notifying
- * all listeners of the ultimate fulfillment or rejection
- * @param {*|Promise} val resolution value
- */
- function promiseResolve(val) {
- if(!handlers) {
- return;
- }
- value = coerce(val);
- scheduleHandlers(handlers, value);
- handlers = undef;
- if (status) {
- value.then(
- function () { status.fulfilled(); },
- function(r) { status.rejected(r); }
- );
- }
- }
- /**
- * Reject this promise with the supplied reason, which will be used verbatim.
- * @param {*} reason reason for the rejection
- */
- function promiseReject(reason) {
- promiseResolve(rejected(reason));
- }
- /**
- * Issue a progress event, notifying all progress listeners
- * @param {*} update progress event payload to pass to all listeners
- */
- function promiseNotify(update) {
- if(handlers) {
- scheduleHandlers(handlers, progressing(update));
- }
- }
- }
- /**
- * Coerces x to a trusted Promise
- *
- * @private
- * @param {*} x thing to coerce
- * @returns {Promise} Guaranteed to return a trusted Promise. If x
- * is trusted, returns x, otherwise, returns a new, trusted, already-resolved
- * Promise whose resolution value is:
- * * the resolution value of x if it's a foreign promise, or
- * * x if it's a value
- */
- function coerce(x) {
- if(x instanceof Promise) {
- return x;
- }
- if (!(x === Object(x) && 'then' in x)) {
- return fulfilled(x);
- }
- return promise(function(resolve, reject, notify) {
- enqueue(function() {
- try {
- // We must check and assimilate in the same tick, but not the
- // current tick, careful only to access promiseOrValue.then once.
- var untrustedThen = x.then;
- if(typeof untrustedThen === 'function') {
- fcall(untrustedThen, x, resolve, reject, notify);
- } else {
- // It's a value, create a fulfilled wrapper
- resolve(fulfilled(x));
- }
- } catch(e) {
- // Something went wrong, reject
- reject(e);
- }
- });
- });
- }
- /**
- * Create an already-fulfilled promise for the supplied value
- * @private
- * @param {*} value
- * @return {Promise} fulfilled promise
- */
- function fulfilled(value) {
- var self = new Promise(function (onFulfilled) {
- try {
- return typeof onFulfilled == 'function'
- ? coerce(onFulfilled(value)) : self;
- } catch (e) {
- return rejected(e);
- }
- }, function() {
- return toFulfilledState(value);
- });
- return self;
- }
- /**
- * Create an already-rejected promise with the supplied rejection reason.
- * @private
- * @param {*} reason
- * @return {Promise} rejected promise
- */
- function rejected(reason) {
- var self = new Promise(function (_, onRejected) {
- try {
- return typeof onRejected == 'function'
- ? coerce(onRejected(reason)) : self;
- } catch (e) {
- return rejected(e);
- }
- }, function() {
- return toRejectedState(reason);
- });
- return self;
- }
- /**
- * Create a progress promise with the supplied update.
- * @private
- * @param {*} update
- * @return {Promise} progress promise
- */
- function progressing(update) {
- var self = new Promise(function (_, __, onProgress) {
- try {
- return typeof onProgress == 'function'
- ? progressing(onProgress(update)) : self;
- } catch (e) {
- return progressing(e);
- }
- });
- return self;
- }
- /**
- * Schedule a task that will process a list of handlers
- * in the next queue drain run.
- * @private
- * @param {Array} handlers queue of handlers to execute
- * @param {*} value passed as the only arg to each handler
- */
- function scheduleHandlers(handlers, value) {
- enqueue(function() {
- var handler, i = 0;
- while (handler = handlers[i++]) {
- handler(value);
- }
- });
- }
- /**
- * Determines if promiseOrValue is a promise or not
- *
- * @param {*} promiseOrValue anything
- * @returns {boolean} true if promiseOrValue is a {@link Promise}
- */
- function isPromise(promiseOrValue) {
- return promiseOrValue && typeof promiseOrValue.then === 'function';
- }
- /**
- * Initiates a competitive race, returning a promise that will resolve when
- * howMany of the supplied promisesOrValues have resolved, or will reject when
- * it becomes impossible for howMany to resolve, for example, when
- * (promisesOrValues.length - howMany) + 1 input promises reject.
- *
- * @param {Array} promisesOrValues array of anything, may contain a mix
- * of promises and values
- * @param howMany {number} number of promisesOrValues to resolve
- * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
- * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
- * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
- * @returns {Promise} promise that will resolve to an array of howMany values that
- * resolved first, or will reject with an array of
- * (promisesOrValues.length - howMany) + 1 rejection reasons.
- */
- function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) {
- return when(promisesOrValues, function(promisesOrValues) {
- return promise(resolveSome).then(onFulfilled, onRejected, onProgress);
- function resolveSome(resolve, reject, notify) {
- var toResolve, toReject, values, reasons, fulfillOne, rejectOne, len, i;
- len = promisesOrValues.length >>> 0;
- toResolve = Math.max(0, Math.min(howMany, len));
- values = [];
- toReject = (len - toResolve) + 1;
- reasons = [];
- // No items in the input, resolve immediately
- if (!toResolve) {
- resolve(values);
- } else {
- rejectOne = function(reason) {
- reasons.push(reason);
- if(!--toReject) {
- fulfillOne = rejectOne = identity;
- reject(reasons);
- }
- };
- fulfillOne = function(val) {
- // This orders the values based on promise resolution order
- values.push(val);
- if (!--toResolve) {
- fulfillOne = rejectOne = identity;
- resolve(values);
- }
- };
- for(i = 0; i < len; ++i) {
- if(i in promisesOrValues) {
- when(promisesOrValues[i], fulfiller, rejecter, notify);
- }
- }
- }
- function rejecter(reason) {
- rejectOne(reason);
- }
- function fulfiller(val) {
- fulfillOne(val);
- }
- }
- });
- }
- /**
- * Initiates a competitive race, returning a promise that will resolve when
- * any one of the supplied promisesOrValues has resolved or will reject when
- * *all* promisesOrValues have rejected.
- *
- * @param {Array|Promise} promisesOrValues array of anything, may contain a mix
- * of {@link Promise}s and values
- * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
- * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
- * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
- * @returns {Promise} promise that will resolve to the value that resolved first, or
- * will reject with an array of all rejected inputs.
- */
- function any(promisesOrValues, onFulfilled, onRejected, onProgress) {
- function unwrapSingleResult(val) {
- return onFulfilled ? onFulfilled(val[0]) : val[0];
- }
- return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress);
- }
- /**
- * Return a promise that will resolve only once all the supplied promisesOrValues
- * have resolved. The resolution value of the returned promise will be an array
- * containing the resolution values of each of the promisesOrValues.
- * @memberOf when
- *
- * @param {Array|Promise} promisesOrValues array of anything, may contain a mix
- * of {@link Promise}s and values
- * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
- * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
- * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
- * @returns {Promise}
- */
- function all(promisesOrValues, onFulfilled, onRejected, onProgress) {
- return _map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress);
- }
- /**
- * Joins multiple promises into a single returned promise.
- * @return {Promise} a promise that will fulfill when *all* the input promises
- * have fulfilled, or will reject when *any one* of the input promises rejects.
- */
- function join(/* ...promises */) {
- return _map(arguments, identity);
- }
- /**
- * Settles all input promises such that they are guaranteed not to
- * be pending once the returned promise fulfills. The returned promise
- * will always fulfill, except in the case where `array` is a promise
- * that rejects.
- * @param {Array|Promise} array or promise for array of promises to settle
- * @returns {Promise} promise that always fulfills with an array of
- * outcome snapshots for each input promise.
- */
- function settle(array) {
- return _map(array, toFulfilledState, toRejectedState);
- }
- /**
- * Promise-aware array map function, similar to `Array.prototype.map()`,
- * but input array may contain promises or values.
- * @param {Array|Promise} array array of anything, may contain promises and values
- * @param {function} mapFunc map function which may return a promise or value
- * @returns {Promise} promise that will fulfill with an array of mapped values
- * or reject if any input promise rejects.
- */
- function map(array, mapFunc) {
- return _map(array, mapFunc);
- }
- /**
- * Internal map that allows a fallback to handle rejections
- * @param {Array|Promise} array array of anything, may contain promises and values
- * @param {function} mapFunc map function which may return a promise or value
- * @param {function?} fallback function to handle rejected promises
- * @returns {Promise} promise that will fulfill with an array of mapped values
- * or reject if any input promise rejects.
- */
- function _map(array, mapFunc, fallback) {
- return when(array, function(array) {
- return promise(resolveMap);
- function resolveMap(resolve, reject, notify) {
- var results, len, toResolve, i;
- // Since we know the resulting length, we can preallocate the results
- // array to avoid array expansions.
- toResolve = len = array.length >>> 0;
- results = [];
- if(!toResolve) {
- resolve(results);
- return;
- }
- // Since mapFunc may be async, get all invocations of it into flight
- for(i = 0; i < len; i++) {
- if(i in array) {
- resolveOne(array[i], i);
- } else {
- --toResolve;
- }
- }
- function resolveOne(item, i) {
- when(item, mapFunc, fallback).then(function(mapped) {
- results[i] = mapped;
- if(!--toResolve) {
- resolve(results);
- }
- }, reject, notify);
- }
- }
- });
- }
- /**
- * Traditional reduce function, similar to `Array.prototype.reduce()`, but
- * input may contain promises and/or values, and reduceFunc
- * may return either a value or a promise, *and* initialValue may
- * be a promise for the starting value.
- *
- * @param {Array|Promise} promise array or promise for an array of anything,
- * may contain a mix of promises and values.
- * @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total),
- * where total is the total number of items being reduced, and will be the same
- * in each call to reduceFunc.
- * @returns {Promise} that will resolve to the final reduced value
- */
- function reduce(promise, reduceFunc /*, initialValue */) {
- var args = fcall(slice, arguments, 1);
- return when(promise, function(array) {
- var total;
- total = array.length;
- // Wrap the supplied reduceFunc with one that handles promises and then
- // delegates to the supplied.
- args[0] = function (current, val, i) {
- return when(current, function (c) {
- return when(val, function (value) {
- return reduceFunc(c, value, i, total);
- });
- });
- };
- return reduceArray.apply(array, args);
- });
- }
- // Snapshot states
- /**
- * Creates a fulfilled state snapshot
- * @private
- * @param {*} x any value
- * @returns {{state:'fulfilled',value:*}}
- */
- function toFulfilledState(x) {
- return { state: 'fulfilled', value: x };
- }
- /**
- * Creates a rejected state snapshot
- * @private
- * @param {*} x any reason
- * @returns {{state:'rejected',reason:*}}
- */
- function toRejectedState(x) {
- return { state: 'rejected', reason: x };
- }
- /**
- * Creates a pending state snapshot
- * @private
- * @returns {{state:'pending'}}
- */
- function toPendingState() {
- return { state: 'pending' };
- }
- //
- // Utilities, etc.
- //
- var reduceArray, slice, fcall, nextTick, handlerQueue,
- setTimeout, funcProto, call, arrayProto, monitorApi, undef;
- //
- // Shared handler queue processing
- //
- // Credit to Twisol (https://github.com/Twisol) for suggesting
- // this type of extensible queue + trampoline approach for
- // next-tick conflation.
- handlerQueue = [];
- /**
- * Enqueue a task. If the queue is not currently scheduled to be
- * drained, schedule it.
- * @param {function} task
- */
- function enqueue(task) {
- if(handlerQueue.push(task) === 1) {
- nextTick(drainQueue);
- }
- }
- /**
- * Drain the handler queue entirely, being careful to allow the
- * queue to be extended while it is being processed, and to continue
- * processing until it is truly empty.
- */
- function drainQueue() {
- var task, i = 0;
- while(task = handlerQueue[i++]) {
- task();
- }
- handlerQueue = [];
- }
- //
- // Capture function and array utils
- //
- /*global setImmediate,process,vertx*/
- // Allow attaching the monitor to when() if env has no console
- monitorApi = typeof console != 'undefined' ? console : when;
- // capture setTimeout to avoid being caught by fake timers used in time based tests
- setTimeout = global.setTimeout;
- // Prefer setImmediate, cascade to node, vertx and finally setTimeout
- nextTick = typeof setImmediate === 'function' ? setImmediate.bind(global)
- : typeof process === 'object' && process.nextTick ? process.nextTick
- : typeof vertx === 'object' ? vertx.runOnLoop // vert.x
- : function(task) { setTimeout(task, 0); }; // fallback
- // Safe function calls
- funcProto = Function.prototype;
- call = funcProto.call;
- fcall = funcProto.bind
- ? call.bind(call)
- : function(f, context) {
- return f.apply(context, slice.call(arguments, 2));
- };
- // Safe array ops
- arrayProto = [];
- slice = arrayProto.slice;
- // ES5 reduce implementation if native not available
- // See: http://es5.github.com/#x15.4.4.21 as there are many
- // specifics and edge cases. ES5 dictates that reduce.length === 1
- // This implementation deviates from ES5 spec in the following ways:
- // 1. It does not check if reduceFunc is a Callable
- reduceArray = arrayProto.reduce ||
- function(reduceFunc /*, initialValue */) {
- /*jshint maxcomplexity: 7*/
- var arr, args, reduced, len, i;
- i = 0;
- arr = Object(this);
- len = arr.length >>> 0;
- args = arguments;
- // If no initialValue, use first item of array (we know length !== 0 here)
- // and adjust i to start at second item
- if(args.length <= 1) {
- // Skip to the first real element in the array
- for(;;) {
- if(i in arr) {
- reduced = arr[i++];
- break;
- }
- // If we reached the end of the array without finding any real
- // elements, it's a TypeError
- if(++i >= len) {
- throw new TypeError();
- }
- }
- } else {
- // If initialValue provided, use it
- reduced = args[1];
- }
- // Do the actual reduce
- for(;i < len; ++i) {
- if(i in arr) {
- reduced = reduceFunc(reduced, arr[i], i, arr);
- }
- }
- return reduced;
- };
- function identity(x) {
- return x;
- }
- return when;
- })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement