when.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. /**
  2. @license
  3. when.js - https://github.com/cujojs/when
  4. MIT License (c) copyright B Cavalier & J Hann
  5. * A lightweight CommonJS Promises/A and when() implementation
  6. * when is part of the cujo.js family of libraries (http://cujojs.com/)
  7. *
  8. * Licensed under the MIT License at:
  9. * http://www.opensource.org/licenses/mit-license.php
  10. *
  11. * @version 1.7.1
  12. */
  13. (function(define) { 'use strict';
  14. define(function () {
  15. var reduceArray, slice, undef;
  16. //
  17. // Public API
  18. //
  19. when.defer = defer; // Create a deferred
  20. when.resolve = resolve; // Create a resolved promise
  21. when.reject = reject; // Create a rejected promise
  22. when.join = join; // Join 2 or more promises
  23. when.all = all; // Resolve a list of promises
  24. when.map = map; // Array.map() for promises
  25. when.reduce = reduce; // Array.reduce() for promises
  26. when.any = any; // One-winner race
  27. when.some = some; // Multi-winner race
  28. when.chain = chain; // Make a promise trigger another resolver
  29. when.isPromise = isPromise; // Determine if a thing is a promise
  30. /**
  31. * Register an observer for a promise or immediate value.
  32. *
  33. * @param {*} promiseOrValue
  34. * @param {function?} [onFulfilled] callback to be called when promiseOrValue is
  35. * successfully fulfilled. If promiseOrValue is an immediate value, callback
  36. * will be invoked immediately.
  37. * @param {function?} [onRejected] callback to be called when promiseOrValue is
  38. * rejected.
  39. * @param {function?} [onProgress] callback to be called when progress updates
  40. * are issued for promiseOrValue.
  41. * @returns {Promise} a new {@link Promise} that will complete with the return
  42. * value of callback or errback or the completion value of promiseOrValue if
  43. * callback and/or errback is not supplied.
  44. */
  45. function when(promiseOrValue, onFulfilled, onRejected, onProgress) {
  46. // Get a trusted promise for the input promiseOrValue, and then
  47. // register promise handlers
  48. return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress);
  49. }
  50. /**
  51. * Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if
  52. * promiseOrValue is a foreign promise, or a new, already-fulfilled {@link Promise}
  53. * whose value is promiseOrValue if promiseOrValue is an immediate value.
  54. *
  55. * @param {*} promiseOrValue
  56. * @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise}
  57. * returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise}
  58. * whose resolution value is:
  59. * * the resolution value of promiseOrValue if it's a foreign promise, or
  60. * * promiseOrValue if it's a value
  61. */
  62. function resolve(promiseOrValue) {
  63. var promise, deferred;
  64. if(promiseOrValue instanceof Promise) {
  65. // It's a when.js promise, so we trust it
  66. promise = promiseOrValue;
  67. } else {
  68. // It's not a when.js promise. See if it's a foreign promise or a value.
  69. if(isPromise(promiseOrValue)) {
  70. // It's a thenable, but we don't know where it came from, so don't trust
  71. // its implementation entirely. Introduce a trusted middleman when.js promise
  72. deferred = defer();
  73. // IMPORTANT: This is the only place when.js should ever call .then() on an
  74. // untrusted promise. Don't expose the return value to the untrusted promise
  75. promiseOrValue.then(
  76. function(value) { deferred.resolve(value); },
  77. function(reason) { deferred.reject(reason); },
  78. function(update) { deferred.progress(update); }
  79. );
  80. promise = deferred.promise;
  81. } else {
  82. // It's a value, not a promise. Create a resolved promise for it.
  83. promise = fulfilled(promiseOrValue);
  84. }
  85. }
  86. return promise;
  87. }
  88. /**
  89. * Returns a rejected promise for the supplied promiseOrValue. The returned
  90. * promise will be rejected with:
  91. * - promiseOrValue, if it is a value, or
  92. * - if promiseOrValue is a promise
  93. * - promiseOrValue's value after it is fulfilled
  94. * - promiseOrValue's reason after it is rejected
  95. * @param {*} promiseOrValue the rejected value of the returned {@link Promise}
  96. * @returns {Promise} rejected {@link Promise}
  97. */
  98. function reject(promiseOrValue) {
  99. return when(promiseOrValue, rejected);
  100. }
  101. /**
  102. * Trusted Promise constructor. A Promise created from this constructor is
  103. * a trusted when.js promise. Any other duck-typed promise is considered
  104. * untrusted.
  105. * @constructor
  106. * @name Promise
  107. */
  108. function Promise(then) {
  109. this.then = then;
  110. }
  111. Promise.prototype = {
  112. /**
  113. * Register a callback that will be called when a promise is
  114. * fulfilled or rejected. Optionally also register a progress handler.
  115. * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress)
  116. * @param {function?} [onFulfilledOrRejected]
  117. * @param {function?} [onProgress]
  118. * @returns {Promise}
  119. */
  120. always: function(onFulfilledOrRejected, onProgress) {
  121. return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress);
  122. },
  123. /**
  124. * Register a rejection handler. Shortcut for .then(undefined, onRejected)
  125. * @param {function?} onRejected
  126. * @returns {Promise}
  127. */
  128. otherwise: function(onRejected) {
  129. return this.then(undef, onRejected);
  130. },
  131. /**
  132. * Shortcut for .then(function() { return value; })
  133. * @param {*} value
  134. * @returns {Promise} a promise that:
  135. * - is fulfilled if value is not a promise, or
  136. * - if value is a promise, will fulfill with its value, or reject
  137. * with its reason.
  138. */
  139. yield: function(value) {
  140. return this.then(function() {
  141. return value;
  142. });
  143. },
  144. /**
  145. * Assumes that this promise will fulfill with an array, and arranges
  146. * for the onFulfilled to be called with the array as its argument list
  147. * i.e. onFulfilled.spread(undefined, array).
  148. * @param {function} onFulfilled function to receive spread arguments
  149. * @returns {Promise}
  150. */
  151. spread: function(onFulfilled) {
  152. return this.then(function(array) {
  153. // array may contain promises, so resolve its contents.
  154. return all(array, function(array) {
  155. return onFulfilled.apply(undef, array);
  156. });
  157. });
  158. }
  159. };
  160. /**
  161. * Create an already-resolved promise for the supplied value
  162. * @private
  163. *
  164. * @param {*} value
  165. * @returns {Promise} fulfilled promise
  166. */
  167. function fulfilled(value) {
  168. var p = new Promise(function(onFulfilled) {
  169. // TODO: Promises/A+ check typeof onFulfilled
  170. try {
  171. return resolve(onFulfilled ? onFulfilled(value) : value);
  172. } catch(e) {
  173. return rejected(e);
  174. }
  175. });
  176. return p;
  177. }
  178. /**
  179. * Create an already-rejected {@link Promise} with the supplied
  180. * rejection reason.
  181. * @private
  182. *
  183. * @param {*} reason
  184. * @returns {Promise} rejected promise
  185. */
  186. function rejected(reason) {
  187. var p = new Promise(function(_, onRejected) {
  188. // TODO: Promises/A+ check typeof onRejected
  189. try {
  190. return onRejected ? resolve(onRejected(reason)) : rejected(reason);
  191. } catch(e) {
  192. return rejected(e);
  193. }
  194. });
  195. return p;
  196. }
  197. /**
  198. * Creates a new, Deferred with fully isolated resolver and promise parts,
  199. * either or both of which may be given out safely to consumers.
  200. * The Deferred itself has the full API: resolve, reject, progress, and
  201. * then. The resolver has resolve, reject, and progress. The promise
  202. * only has then.
  203. *
  204. * @returns {Deferred}
  205. */
  206. function defer() {
  207. var deferred, promise, handlers, progressHandlers,
  208. _then, _progress, _resolve;
  209. /**
  210. * The promise for the new deferred
  211. * @type {Promise}
  212. */
  213. promise = new Promise(then);
  214. /**
  215. * The full Deferred object, with {@link Promise} and {@link Resolver} parts
  216. * @class Deferred
  217. * @name Deferred
  218. */
  219. deferred = {
  220. then: then, // DEPRECATED: use deferred.promise.then
  221. resolve: promiseResolve,
  222. reject: promiseReject,
  223. // TODO: Consider renaming progress() to notify()
  224. progress: promiseProgress,
  225. promise: promise,
  226. resolver: {
  227. resolve: promiseResolve,
  228. reject: promiseReject,
  229. progress: promiseProgress
  230. }
  231. };
  232. handlers = [];
  233. progressHandlers = [];
  234. /**
  235. * Pre-resolution then() that adds the supplied callback, errback, and progback
  236. * functions to the registered listeners
  237. * @private
  238. *
  239. * @param {function?} [onFulfilled] resolution handler
  240. * @param {function?} [onRejected] rejection handler
  241. * @param {function?} [onProgress] progress handler
  242. */
  243. _then = function(onFulfilled, onRejected, onProgress) {
  244. // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress
  245. var deferred, progressHandler;
  246. deferred = defer();
  247. progressHandler = typeof onProgress === 'function'
  248. ? function(update) {
  249. try {
  250. // Allow progress handler to transform progress event
  251. deferred.progress(onProgress(update));
  252. } catch(e) {
  253. // Use caught value as progress
  254. deferred.progress(e);
  255. }
  256. }
  257. : function(update) { deferred.progress(update); };
  258. handlers.push(function(promise) {
  259. promise.then(onFulfilled, onRejected)
  260. .then(deferred.resolve, deferred.reject, progressHandler);
  261. });
  262. progressHandlers.push(progressHandler);
  263. return deferred.promise;
  264. };
  265. /**
  266. * Issue a progress event, notifying all progress listeners
  267. * @private
  268. * @param {*} update progress event payload to pass to all listeners
  269. */
  270. _progress = function(update) {
  271. processQueue(progressHandlers, update);
  272. return update;
  273. };
  274. /**
  275. * Transition from pre-resolution state to post-resolution state, notifying
  276. * all listeners of the resolution or rejection
  277. * @private
  278. * @param {*} value the value of this deferred
  279. */
  280. _resolve = function(value) {
  281. value = resolve(value);
  282. // Replace _then with one that directly notifies with the result.
  283. _then = value.then;
  284. // Replace _resolve so that this Deferred can only be resolved once
  285. _resolve = resolve;
  286. // Make _progress a noop, to disallow progress for the resolved promise.
  287. _progress = noop;
  288. // Notify handlers
  289. processQueue(handlers, value);
  290. // Free progressHandlers array since we'll never issue progress events
  291. progressHandlers = handlers = undef;
  292. return value;
  293. };
  294. return deferred;
  295. /**
  296. * Wrapper to allow _then to be replaced safely
  297. * @param {function?} [onFulfilled] resolution handler
  298. * @param {function?} [onRejected] rejection handler
  299. * @param {function?} [onProgress] progress handler
  300. * @returns {Promise} new promise
  301. */
  302. function then(onFulfilled, onRejected, onProgress) {
  303. // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress
  304. return _then(onFulfilled, onRejected, onProgress);
  305. }
  306. /**
  307. * Wrapper to allow _resolve to be replaced
  308. */
  309. function promiseResolve(val) {
  310. return _resolve(val);
  311. }
  312. /**
  313. * Wrapper to allow _reject to be replaced
  314. */
  315. function promiseReject(err) {
  316. return _resolve(rejected(err));
  317. }
  318. /**
  319. * Wrapper to allow _progress to be replaced
  320. */
  321. function promiseProgress(update) {
  322. return _progress(update);
  323. }
  324. }
  325. /**
  326. * Determines if promiseOrValue is a promise or not. Uses the feature
  327. * test from http://wiki.commonjs.org/wiki/Promises/A to determine if
  328. * promiseOrValue is a promise.
  329. *
  330. * @param {*} promiseOrValue anything
  331. * @returns {boolean} true if promiseOrValue is a {@link Promise}
  332. */
  333. function isPromise(promiseOrValue) {
  334. return promiseOrValue && typeof promiseOrValue.then === 'function';
  335. }
  336. /**
  337. * Initiates a competitive race, returning a promise that will resolve when
  338. * howMany of the supplied promisesOrValues have resolved, or will reject when
  339. * it becomes impossible for howMany to resolve, for example, when
  340. * (promisesOrValues.length - howMany) + 1 input promises reject.
  341. *
  342. * @param {Array} promisesOrValues array of anything, may contain a mix
  343. * of promises and values
  344. * @param howMany {number} number of promisesOrValues to resolve
  345. * @param {function?} [onFulfilled] resolution handler
  346. * @param {function?} [onRejected] rejection handler
  347. * @param {function?} [onProgress] progress handler
  348. * @returns {Promise} promise that will resolve to an array of howMany values that
  349. * resolved first, or will reject with an array of (promisesOrValues.length - howMany) + 1
  350. * rejection reasons.
  351. */
  352. function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) {
  353. checkCallbacks(2, arguments);
  354. return when(promisesOrValues, function(promisesOrValues) {
  355. var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, progress, len, i;
  356. len = promisesOrValues.length >>> 0;
  357. toResolve = Math.max(0, Math.min(howMany, len));
  358. values = [];
  359. toReject = (len - toResolve) + 1;
  360. reasons = [];
  361. deferred = defer();
  362. // No items in the input, resolve immediately
  363. if (!toResolve) {
  364. deferred.resolve(values);
  365. } else {
  366. progress = deferred.progress;
  367. rejectOne = function(reason) {
  368. reasons.push(reason);
  369. if(!--toReject) {
  370. fulfillOne = rejectOne = noop;
  371. deferred.reject(reasons);
  372. }
  373. };
  374. fulfillOne = function(val) {
  375. // This orders the values based on promise resolution order
  376. // Another strategy would be to use the original position of
  377. // the corresponding promise.
  378. values.push(val);
  379. if (!--toResolve) {
  380. fulfillOne = rejectOne = noop;
  381. deferred.resolve(values);
  382. }
  383. };
  384. for(i = 0; i < len; ++i) {
  385. if(i in promisesOrValues) {
  386. when(promisesOrValues[i], fulfiller, rejecter, progress);
  387. }
  388. }
  389. }
  390. return deferred.then(onFulfilled, onRejected, onProgress);
  391. function rejecter(reason) {
  392. rejectOne(reason);
  393. }
  394. function fulfiller(val) {
  395. fulfillOne(val);
  396. }
  397. });
  398. }
  399. /**
  400. * Initiates a competitive race, returning a promise that will resolve when
  401. * any one of the supplied promisesOrValues has resolved or will reject when
  402. * *all* promisesOrValues have rejected.
  403. *
  404. * @param {Array|Promise} promisesOrValues array of anything, may contain a mix
  405. * of {@link Promise}s and values
  406. * @param {function?} [onFulfilled] resolution handler
  407. * @param {function?} [onRejected] rejection handler
  408. * @param {function?} [onProgress] progress handler
  409. * @returns {Promise} promise that will resolve to the value that resolved first, or
  410. * will reject with an array of all rejected inputs.
  411. */
  412. function any(promisesOrValues, onFulfilled, onRejected, onProgress) {
  413. function unwrapSingleResult(val) {
  414. return onFulfilled ? onFulfilled(val[0]) : val[0];
  415. }
  416. return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress);
  417. }
  418. /**
  419. * Return a promise that will resolve only once all the supplied promisesOrValues
  420. * have resolved. The resolution value of the returned promise will be an array
  421. * containing the resolution values of each of the promisesOrValues.
  422. * @memberOf when
  423. *
  424. * @param {Array|Promise} promisesOrValues array of anything, may contain a mix
  425. * of {@link Promise}s and values
  426. * @param {function?} [onFulfilled] resolution handler
  427. * @param {function?} [onRejected] rejection handler
  428. * @param {function?} [onProgress] progress handler
  429. * @returns {Promise}
  430. */
  431. function all(promisesOrValues, onFulfilled, onRejected, onProgress) {
  432. checkCallbacks(1, arguments);
  433. return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress);
  434. }
  435. /**
  436. * Joins multiple promises into a single returned promise.
  437. * @returns {Promise} a promise that will fulfill when *all* the input promises
  438. * have fulfilled, or will reject when *any one* of the input promises rejects.
  439. */
  440. function join(/* ...promises */) {
  441. return map(arguments, identity);
  442. }
  443. /**
  444. * Traditional map function, similar to `Array.prototype.map()`, but allows
  445. * input to contain {@link Promise}s and/or values, and mapFunc may return
  446. * either a value or a {@link Promise}
  447. *
  448. * @param {Array|Promise} promise array of anything, may contain a mix
  449. * of {@link Promise}s and values
  450. * @param {function} mapFunc mapping function mapFunc(value) which may return
  451. * either a {@link Promise} or value
  452. * @returns {Promise} a {@link Promise} that will resolve to an array containing
  453. * the mapped output values.
  454. */
  455. function map(promise, mapFunc) {
  456. return when(promise, function(array) {
  457. var results, len, toResolve, resolve, i, d;
  458. // Since we know the resulting length, we can preallocate the results
  459. // array to avoid array expansions.
  460. toResolve = len = array.length >>> 0;
  461. results = [];
  462. d = defer();
  463. if(!toResolve) {
  464. d.resolve(results);
  465. } else {
  466. resolve = function resolveOne(item, i) {
  467. when(item, mapFunc).then(function(mapped) {
  468. results[i] = mapped;
  469. if(!--toResolve) {
  470. d.resolve(results);
  471. }
  472. }, d.reject);
  473. };
  474. // Since mapFunc may be async, get all invocations of it into flight
  475. for(i = 0; i < len; i++) {
  476. if(i in array) {
  477. resolve(array[i], i);
  478. } else {
  479. --toResolve;
  480. }
  481. }
  482. }
  483. return d.promise;
  484. });
  485. }
  486. /**
  487. * Traditional reduce function, similar to `Array.prototype.reduce()`, but
  488. * input may contain promises and/or values, and reduceFunc
  489. * may return either a value or a promise, *and* initialValue may
  490. * be a promise for the starting value.
  491. *
  492. * @param {Array|Promise} promise array or promise for an array of anything,
  493. * may contain a mix of promises and values.
  494. * @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total),
  495. * where total is the total number of items being reduced, and will be the same
  496. * in each call to reduceFunc.
  497. * @returns {Promise} that will resolve to the final reduced value
  498. */
  499. function reduce(promise, reduceFunc /*, initialValue */) {
  500. var args = slice.call(arguments, 1);
  501. return when(promise, function(array) {
  502. var total;
  503. total = array.length;
  504. // Wrap the supplied reduceFunc with one that handles promises and then
  505. // delegates to the supplied.
  506. args[0] = function (current, val, i) {
  507. return when(current, function (c) {
  508. return when(val, function (value) {
  509. return reduceFunc(c, value, i, total);
  510. });
  511. });
  512. };
  513. return reduceArray.apply(array, args);
  514. });
  515. }
  516. /**
  517. * Ensure that resolution of promiseOrValue will trigger resolver with the
  518. * value or reason of promiseOrValue, or instead with resolveValue if it is provided.
  519. *
  520. * @param promiseOrValue
  521. * @param {Object} resolver
  522. * @param {function} resolver.resolve
  523. * @param {function} resolver.reject
  524. * @param {*} [resolveValue]
  525. * @returns {Promise}
  526. */
  527. function chain(promiseOrValue, resolver, resolveValue) {
  528. var useResolveValue = arguments.length > 2;
  529. return when(promiseOrValue,
  530. function(val) {
  531. val = useResolveValue ? resolveValue : val;
  532. resolver.resolve(val);
  533. return val;
  534. },
  535. function(reason) {
  536. resolver.reject(reason);
  537. return rejected(reason);
  538. },
  539. resolver.progress
  540. );
  541. }
  542. //
  543. // Utility functions
  544. //
  545. /**
  546. * Apply all functions in queue to value
  547. * @param {Array} queue array of functions to execute
  548. * @param {*} value argument passed to each function
  549. */
  550. function processQueue(queue, value) {
  551. var handler, i = 0;
  552. while (handler = queue[i++]) {
  553. handler(value);
  554. }
  555. }
  556. /**
  557. * Helper that checks arrayOfCallbacks to ensure that each element is either
  558. * a function, or null or undefined.
  559. * @private
  560. * @param {number} start index at which to start checking items in arrayOfCallbacks
  561. * @param {Array} arrayOfCallbacks array to check
  562. * @throws {Error} if any element of arrayOfCallbacks is something other than
  563. * a functions, null, or undefined.
  564. */
  565. function checkCallbacks(start, arrayOfCallbacks) {
  566. // TODO: Promises/A+ update type checking and docs
  567. var arg, i = arrayOfCallbacks.length;
  568. while(i > start) {
  569. arg = arrayOfCallbacks[--i];
  570. if (arg != null && typeof arg != 'function') {
  571. throw new Error('arg '+i+' must be a function');
  572. }
  573. }
  574. }
  575. /**
  576. * No-Op function used in method replacement
  577. * @private
  578. */
  579. function noop() {}
  580. slice = [].slice;
  581. // ES5 reduce implementation if native not available
  582. // See: http://es5.github.com/#x15.4.4.21 as there are many
  583. // specifics and edge cases.
  584. reduceArray = [].reduce ||
  585. function(reduceFunc /*, initialValue */) {
  586. /*jshint maxcomplexity: 7*/
  587. // ES5 dictates that reduce.length === 1
  588. // This implementation deviates from ES5 spec in the following ways:
  589. // 1. It does not check if reduceFunc is a Callable
  590. var arr, args, reduced, len, i;
  591. i = 0;
  592. // This generates a jshint warning, despite being valid
  593. // "Missing 'new' prefix when invoking a constructor."
  594. // See https://github.com/jshint/jshint/issues/392
  595. arr = Object(this);
  596. len = arr.length >>> 0;
  597. args = arguments;
  598. // If no initialValue, use first item of array (we know length !== 0 here)
  599. // and adjust i to start at second item
  600. if(args.length <= 1) {
  601. // Skip to the first real element in the array
  602. for(;;) {
  603. if(i in arr) {
  604. reduced = arr[i++];
  605. break;
  606. }
  607. // If we reached the end of the array without finding any real
  608. // elements, it's a TypeError
  609. if(++i >= len) {
  610. throw new TypeError();
  611. }
  612. }
  613. } else {
  614. // If initialValue provided, use it
  615. reduced = args[1];
  616. }
  617. // Do the actual reduce
  618. for(;i < len; ++i) {
  619. // Skip holes
  620. if(i in arr) {
  621. reduced = reduceFunc(reduced, arr[i], i, arr);
  622. }
  623. }
  624. return reduced;
  625. };
  626. function identity(x) {
  627. return x;
  628. }
  629. return when;
  630. });
  631. })(typeof define == 'function' && define.amd
  632. ? define
  633. : function (factory) { typeof exports === 'object'
  634. ? (module.exports = factory())
  635. : (this.when = factory());
  636. }
  637. // Boilerplate for AMD, Node, and browser global
  638. );