-
-
Save briancavalier/2b01f13c4bf47458e1c8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @license MIT License (c) copyright B Cavalier & J Hann */ | |
/** | |
* 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 | |
* | |
* @version 2.x.x | |
*/ | |
(function(define) { 'use strict'; | |
define(function () { | |
var reduceArray, slice, | |
nextTick, handlerQueue, queueProcessLimit, maxQueueProcessLimit, | |
undef; | |
// | |
// Public API | |
// | |
when.defer = defer; // Create a deferred | |
when.resolve = resolve; // Create a resolved promise | |
when.reject = reject; // Create a rejected promise | |
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.any = any; // One-winner race | |
when.some = some; // Multi-winner race | |
when.chain = chain; // Make a promise trigger another resolver | |
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); | |
} | |
/** | |
* Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if | |
* promiseOrValue is a foreign promise, or a new, already-fulfilled {@link Promise} | |
* whose value is promiseOrValue if promiseOrValue is an immediate value. | |
* | |
* @param {*} promiseOrValue | |
* @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise} | |
* returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise} | |
* whose resolution value is: | |
* * the resolution value of promiseOrValue if it's a foreign promise, or | |
* * promiseOrValue if it's a value | |
*/ | |
function promiseFor(promiseOrValue) { | |
var promise, deferred; | |
if(promiseOrValue instanceof Promise) { | |
// It's a when.js promise, so we trust it | |
promise = promiseOrValue; | |
} else { | |
// It's not a when.js promise. See if it's a foreign promise or a value. | |
if(isPromise(promiseOrValue)) { | |
// It's a thenable, but we don't know where it came from, so don't trust | |
// its implementation entirely. Introduce a trusted middleman when.js promise | |
deferred = defer(); | |
// IMPORTANT: This is the only place when.js should ever call .then() on an | |
// untrusted promise. Don't expose the return value to the untrusted promise | |
promiseOrValue.then( | |
function(value) { deferred.resolve(value); }, | |
function(reason) { deferred.reject(reason); }, | |
function(update) { deferred.progress(update); } | |
); | |
promise = deferred.promise; | |
} else { | |
// It's a value, not a promise. Create a resolved promise for it. | |
promise = fulfilled(promiseOrValue); | |
} | |
} | |
return promise; | |
} | |
/** | |
* Returns a fulfilled promise. If promiseOrValue is a value, it will be the fulfillment | |
* value of the returned promise. If promiseOrValue is a promise, the returned promise will | |
* parallel the state and value/reason of promiseOrValue. | |
* @param {*} promiseOrValue the fulfillment value or a promise with whose state will be paralleled | |
* @return {Promise} fulfilled promise or pending promise paralleling the state of promiseOrValue. | |
*/ | |
function resolve(promiseOrValue) { | |
return defer().resolve(promiseOrValue); | |
} | |
/** | |
* 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); | |
} | |
/** | |
* 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) { | |
this.then = then; | |
} | |
Promise.prototype = { | |
/** | |
* Register a callback that will be called when a promise is | |
* fulfilled or rejected. Optionally also register a progress handler. | |
* Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress) | |
* @param {function?} [onFulfilledOrRejected] | |
* @param {function?} [onProgress] | |
* @return {Promise} | |
*/ | |
always: function(onFulfilledOrRejected, onProgress) { | |
return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); | |
}, | |
/** | |
* Register a rejection handler. Shortcut for .then(undefined, onRejected) | |
* @param {function?} onRejected | |
* @return {Promise} | |
*/ | |
otherwise: function(onRejected) { | |
return this.then(undef, onRejected); | |
}, | |
/** | |
* 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.spread(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); | |
}); | |
}); | |
} | |
}; | |
/** | |
* Create an already-resolved promise for the supplied value | |
* @private | |
* | |
* @param {*} value | |
* @return {Promise} fulfilled promise | |
*/ | |
function fulfilled(value) { | |
var p = new Promise(function(onFulfilled) { | |
try { | |
return promiseFor(typeof onFulfilled == 'function' ? onFulfilled(value) : value); | |
} catch(e) { | |
return rejected(e); | |
} | |
}); | |
return p; | |
} | |
/** | |
* Create an already-rejected {@link Promise} with the supplied | |
* rejection reason. | |
* @private | |
* | |
* @param {*} reason | |
* @return {Promise} rejected promise | |
*/ | |
function rejected(reason) { | |
var p = new Promise(function(_, onRejected) { | |
try { | |
return promiseFor(typeof onRejected == 'function' ? onRejected(reason) : rejected(reason)); | |
} catch(e) { | |
return rejected(e); | |
} | |
}); | |
return p; | |
} | |
/** | |
* Creates a new, Deferred with fully isolated resolver and promise parts, | |
* either or both of which may be given out safely to consumers. | |
* The Deferred itself has the full API: resolve, reject, progress, and | |
* then. The resolver has resolve, reject, and progress. The promise | |
* only has then. | |
* | |
* @return {Deferred} | |
*/ | |
function defer() { | |
var deferred, promise, handlers, progressHandlers, | |
_bind, _progress, _resolve; | |
/** | |
* The promise for the new deferred | |
* @type {Promise} | |
*/ | |
promise = new Promise(then); | |
/** | |
* The full Deferred object, with {@link Promise} and {@link Resolver} parts | |
* @class Deferred | |
* @name Deferred | |
*/ | |
deferred = { | |
then: then, // DEPRECATED: use deferred.promise.then | |
resolve: promiseResolve, | |
reject: promiseReject, | |
// TODO: Consider renaming progress() to notify() | |
progress: promiseProgress, | |
promise: promise, | |
resolver: { | |
resolve: promiseResolve, | |
reject: promiseReject, | |
progress: promiseProgress | |
} | |
}; | |
handlers = []; | |
progressHandlers = []; | |
_bind = function(onFulfilled, onRejected, onProgress, next) { | |
var progressHandler = typeof onProgress === 'function' | |
? function(update) { | |
try { | |
// Allow progress handler to transform progress event | |
next.progress(onProgress(update)); | |
} catch(e) { | |
// Use caught value as progress | |
next.progress(e); | |
} | |
} | |
: next.progress; | |
handlers.push(function(promise) { | |
promise.then(onFulfilled, onRejected).then( | |
function(value) { next.resolve(value); }, | |
function(reason) { next.reject(reason); }, | |
progressHandler | |
); | |
}); | |
progressHandlers.push(progressHandler); | |
}; | |
/** | |
* Issue a progress event, notifying all progress listeners | |
* @private | |
* @param {*} update progress event payload to pass to all listeners | |
*/ | |
_progress = function(update) { | |
scheduleHandlers(progressHandlers, update); | |
return update; | |
}; | |
/** | |
* Transition from pre-resolution state to post-resolution state, notifying | |
* all listeners of the resolution or rejection | |
* @private | |
* @param {*} value the value of this deferred | |
*/ | |
_resolve = function(value) { | |
value = promiseFor(value); | |
// Replace _resolve so that this Deferred can only be completed once | |
// Make _progress a noop, to disallow progress for the resolved promise. | |
_resolve = resolve; | |
_progress = noop; | |
// Make _bind invoke callbacks "immediately" | |
_bind = function(fulfilled, rejected, _, next) { | |
enqueue(function() { | |
value.then(fulfilled, rejected).then( | |
function(value) { next.resolve(value); }, | |
function(reason) { next.reject(reason); }, | |
function(update) { next.progress(update); } | |
); | |
}); | |
}; | |
// Notify handlers | |
scheduleHandlers(handlers, value); | |
handlers = progressHandlers = undef; | |
return promise; | |
}; | |
return deferred; | |
/** | |
* Wrapper to allow _then to be replaced safely | |
* @param [onFulfilled] {Function} resolution handler | |
* @param [onRejected] {Function} rejection handler | |
* @param [onProgress] {Function} progress handler | |
* @return {Promise} new Promise | |
*/ | |
function then(onFulfilled, onRejected, onProgress) { | |
var deferred = defer(); | |
_bind(onFulfilled, onRejected, onProgress, deferred); | |
return deferred.promise; | |
} | |
/** | |
* Wrapper to allow _resolve to be replaced | |
*/ | |
function promiseResolve(val) { | |
return _resolve(val); | |
} | |
/** | |
* Wrapper to allow _reject to be replaced | |
*/ | |
function promiseReject(reason) { | |
return _resolve(rejected(reason)); | |
} | |
/** | |
* Wrapper to allow _progress to be replaced | |
*/ | |
function promiseProgress(update) { | |
return _progress(update); | |
} | |
} | |
/** | |
* Determines if promiseOrValue is a promise or not. Uses the feature | |
* test from http://wiki.commonjs.org/wiki/Promises/A to determine if | |
* promiseOrValue is a promise. | |
* | |
* @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] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @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) { | |
checkCallbacks(2, arguments); | |
return when(promisesOrValues, function(promisesOrValues) { | |
var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, progress, len, i; | |
len = promisesOrValues.length >>> 0; | |
toResolve = Math.max(0, Math.min(howMany, len)); | |
values = []; | |
toReject = (len - toResolve) + 1; | |
reasons = []; | |
deferred = defer(); | |
// No items in the input, resolve immediately | |
if (!toResolve) { | |
deferred.resolve(values); | |
} else { | |
progress = deferred.progress; | |
rejectOne = function(reason) { | |
reasons.push(reason); | |
if(!--toReject) { | |
fulfillOne = rejectOne = noop; | |
deferred.reject(reasons.slice()); | |
} | |
}; | |
fulfillOne = function(val) { | |
// This orders the values based on promise resolution order | |
// Another strategy would be to use the original position of | |
// the corresponding promise. | |
values.push(val); | |
if (!--toResolve) { | |
fulfillOne = rejectOne = noop; | |
deferred.resolve(values.slice()); | |
} | |
}; | |
for(i = 0; i < len; ++i) { | |
if(i in promisesOrValues) { | |
when(promisesOrValues[i], fulfiller, rejecter, progress); | |
} | |
} | |
} | |
return deferred.promise.then(onFulfilled, onRejected, onProgress); | |
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] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @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] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @returns {Promise} | |
*/ | |
function all(promisesOrValues, onFulfilled, onRejected, onProgress) { | |
checkCallbacks(1, arguments); | |
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); | |
} | |
/** | |
* Traditional map function, similar to `Array.prototype.map()`, but allows | |
* input to contain {@link Promise}s and/or values, and mapFunc may return | |
* either a value or a {@link Promise} | |
* | |
* @param {Array|Promise} promise array of anything, may contain a mix | |
* of {@link Promise}s and values | |
* @param {function} mapFunc mapping function mapFunc(value) which may return | |
* either a {@link Promise} or value | |
* @returns {Promise} a {@link Promise} that will resolve to an array containing | |
* the mapped output values. | |
*/ | |
function map(promise, mapFunc) { | |
return when(promise, function(array) { | |
var results, len, toResolve, resolve, i, d; | |
// Since we know the resulting length, we can preallocate the results | |
// array to avoid array expansions. | |
toResolve = len = array.length >>> 0; | |
results = []; | |
d = defer(); | |
if(!toResolve) { | |
d.resolve(results); | |
} else { | |
resolve = function resolveOne(item, i) { | |
when(item, mapFunc).then(function(mapped) { | |
results[i] = mapped; | |
if(!--toResolve) { | |
d.resolve(results); | |
} | |
}, d.reject); | |
}; | |
// Since mapFunc may be async, get all invocations of it into flight | |
for(i = 0; i < len; i++) { | |
if(i in array) { | |
resolve(array[i], i); | |
} else { | |
--toResolve; | |
} | |
} | |
} | |
return d.promise; | |
}); | |
} | |
/** | |
* 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 = slice.call(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); | |
}); | |
} | |
/** | |
* Ensure that resolution of promiseOrValue will trigger resolver with the | |
* value or reason of promiseOrValue, or instead with resolveValue if it is provided. | |
* | |
* @param promiseOrValue | |
* @param {Object} resolver | |
* @param {function} resolver.resolve | |
* @param {function} resolver.reject | |
* @param {*} [resolveValue] | |
* @returns {Promise} | |
*/ | |
function chain(promiseOrValue, resolver, resolveValue) { | |
var useResolveValue = arguments.length > 2; | |
return when(promiseOrValue, | |
function(val) { | |
val = useResolveValue ? resolveValue : val; | |
resolver.resolve(val); | |
return val; | |
}, | |
function(reason) { | |
resolver.reject(reason); | |
return rejected(reason); | |
}, | |
resolver.progress | |
); | |
} | |
// | |
// Handler queue processing | |
// | |
// | |
// Experiment | |
// Can we process pending handlers for all resolved promises | |
// the the *very next tick* without introducing subsequent ticks | |
// | |
// Example: | |
// p.then(f).then(g).then(h) | |
// | |
// It should be possible to process f, g, and h in the tick | |
// immediately after the one where the above statement executes. | |
// However, when.js (and afaik, all other async promise impls) will | |
// process them in separate ticks. | |
// | |
// It may be important to be friendly to the platform's tick/timer | |
// queue. Allowing when.js' queue to extend while it is also | |
// being processed, could potentially starve the platform tick | |
// queue. A strategy where we drain up to N handlers from | |
// the queue, then schedule another drain, and so on, will yield | |
// some time back to the platform and be more friendly. | |
// | |
// Finding N may be tricky. Allowing N to vary adaptively may | |
// be a good solution. | |
/*global setImmediate:true */ | |
nextTick = typeof setImmediate === 'function' ? setImmediate | |
: typeof process === 'object' ? process.nextTick | |
: function(task) { setTimeout(task, 0); }; | |
// NOTE: For sync testing only: | |
// nextTick = function(t) { t(); }; | |
handlerQueue = []; | |
queueProcessLimit = 1000; | |
maxQueueProcessLimit = 10000; | |
/** | |
* Schedule a task that will process a list of handlers | |
* in the next queue drain run. | |
* @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); | |
} | |
}); | |
} | |
/** | |
* 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) { | |
scheduleDrainQueue(); | |
} | |
} | |
/** | |
* Schedule the queue to be drained in the next tick. | |
*/ | |
function scheduleDrainQueue() { | |
nextTick(drainQueue); | |
} | |
/** | |
* Drain the handler queue entirely or partially, 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; | |
// Drain up to queueProcessLimit items to avoid starving the tick/timer queue | |
while(i < queueProcessLimit && (task = handlerQueue[i++])) { | |
task(); | |
} | |
if (handlerQueue.length > i) { | |
// If there are handlers remaining, schedule another drain, but | |
// also increase the max number of handlers (to a point) that'll be drained | |
// from now on. | |
queueProcessLimit = Math.max(queueProcessLimit * 2, maxQueueProcessLimit); | |
handlerQueue = handlerQueue.slice(i, handlerQueue.length); | |
scheduleDrainQueue(); | |
} else { | |
handlerQueue = []; | |
} | |
} | |
// | |
// Utility functions | |
// | |
/** | |
* Helper that checks arrayOfCallbacks to ensure that each element is either | |
* a function, or null or undefined. | |
* @private | |
* @param {number} start index at which to start checking items in arrayOfCallbacks | |
* @param {Array} arrayOfCallbacks array to check | |
* @throws {Error} if any element of arrayOfCallbacks is something other than | |
* a functions, null, or undefined. | |
*/ | |
function checkCallbacks(start, arrayOfCallbacks) { | |
// TODO: Promises/A+ update type checking and docs | |
var arg, i = arrayOfCallbacks.length; | |
while(i > start) { | |
arg = arrayOfCallbacks[--i]; | |
if (arg != null && typeof arg != 'function') { | |
throw new Error('arg '+i+' must be a function'); | |
} | |
} | |
} | |
/** | |
* No-Op function used in method replacement | |
* @private | |
*/ | |
function noop() {} | |
slice = [].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. | |
reduceArray = [].reduce || | |
function(reduceFunc /*, initialValue */) { | |
/*jshint maxcomplexity: 7*/ | |
// 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 | |
var arr, args, reduced, len, i; | |
i = 0; | |
// This generates a jshint warning, despite being valid | |
// "Missing 'new' prefix when invoking a constructor." | |
// See https://github.com/jshint/jshint/issues/392 | |
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) { | |
// Skip holes | |
if(i in arr) { | |
reduced = reduceFunc(reduced, arr[i], i, arr); | |
} | |
} | |
return reduced; | |
}; | |
function identity(x) { | |
return x; | |
} | |
return when; | |
}); | |
})(typeof define == 'function' && define.amd | |
? define | |
: function (factory) { typeof exports === 'object' | |
? (module.exports = factory()) | |
: (this.when = factory()); | |
} | |
// Boilerplate for AMD, Node, and browser global | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment