when-8d13db60.js 29 KB

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