when.js 21 KB

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