| /** @license MIT License (c) copyright 2010-2014 original author or authors */ |
| /** @author Brian Cavalier */ |
| /** @author John Hann */ |
| |
| (function(define) { 'use strict'; |
| define(function(require) { |
| |
| var state = require('../state'); |
| var applier = require('../apply'); |
| |
| return function array(Promise) { |
| |
| var applyFold = applier(Promise); |
| var toPromise = Promise.resolve; |
| var all = Promise.all; |
| |
| var ar = Array.prototype.reduce; |
| var arr = Array.prototype.reduceRight; |
| var slice = Array.prototype.slice; |
| |
| // Additional array combinators |
| |
| Promise.any = any; |
| Promise.some = some; |
| Promise.settle = settle; |
| |
| Promise.map = map; |
| Promise.filter = filter; |
| Promise.reduce = reduce; |
| Promise.reduceRight = reduceRight; |
| |
| /** |
| * When this promise fulfills with an array, do |
| * onFulfilled.apply(void 0, array) |
| * @param {function} onFulfilled function to apply |
| * @returns {Promise} promise for the result of applying onFulfilled |
| */ |
| Promise.prototype.spread = function(onFulfilled) { |
| return this.then(all).then(function(array) { |
| return onFulfilled.apply(this, array); |
| }); |
| }; |
| |
| return Promise; |
| |
| /** |
| * One-winner competitive race. |
| * Return a promise that will fulfill when one of the promises |
| * in the input array fulfills, or will reject when all promises |
| * have rejected. |
| * @param {array} promises |
| * @returns {Promise} promise for the first fulfilled value |
| */ |
| function any(promises) { |
| var p = Promise._defer(); |
| var resolver = p._handler; |
| var l = promises.length>>>0; |
| |
| var pending = l; |
| var errors = []; |
| |
| for (var h, x, i = 0; i < l; ++i) { |
| x = promises[i]; |
| if(x === void 0 && !(i in promises)) { |
| --pending; |
| continue; |
| } |
| |
| h = Promise._handler(x); |
| if(h.state() > 0) { |
| resolver.become(h); |
| Promise._visitRemaining(promises, i, h); |
| break; |
| } else { |
| h.visit(resolver, handleFulfill, handleReject); |
| } |
| } |
| |
| if(pending === 0) { |
| resolver.reject(new RangeError('any(): array must not be empty')); |
| } |
| |
| return p; |
| |
| function handleFulfill(x) { |
| /*jshint validthis:true*/ |
| errors = null; |
| this.resolve(x); // this === resolver |
| } |
| |
| function handleReject(e) { |
| /*jshint validthis:true*/ |
| if(this.resolved) { // this === resolver |
| return; |
| } |
| |
| errors.push(e); |
| if(--pending === 0) { |
| this.reject(errors); |
| } |
| } |
| } |
| |
| /** |
| * N-winner competitive race |
| * Return a promise that will fulfill when n input promises have |
| * fulfilled, or will reject when it becomes impossible for n |
| * input promises to fulfill (ie when promises.length - n + 1 |
| * have rejected) |
| * @param {array} promises |
| * @param {number} n |
| * @returns {Promise} promise for the earliest n fulfillment values |
| * |
| * @deprecated |
| */ |
| function some(promises, n) { |
| /*jshint maxcomplexity:7*/ |
| var p = Promise._defer(); |
| var resolver = p._handler; |
| |
| var results = []; |
| var errors = []; |
| |
| var l = promises.length>>>0; |
| var nFulfill = 0; |
| var nReject; |
| var x, i; // reused in both for() loops |
| |
| // First pass: count actual array items |
| for(i=0; i<l; ++i) { |
| x = promises[i]; |
| if(x === void 0 && !(i in promises)) { |
| continue; |
| } |
| ++nFulfill; |
| } |
| |
| // Compute actual goals |
| n = Math.max(n, 0); |
| nReject = (nFulfill - n + 1); |
| nFulfill = Math.min(n, nFulfill); |
| |
| if(n > nFulfill) { |
| resolver.reject(new RangeError('some(): array must contain at least ' |
| + n + ' item(s), but had ' + nFulfill)); |
| } else if(nFulfill === 0) { |
| resolver.resolve(results); |
| } |
| |
| // Second pass: observe each array item, make progress toward goals |
| for(i=0; i<l; ++i) { |
| x = promises[i]; |
| if(x === void 0 && !(i in promises)) { |
| continue; |
| } |
| |
| Promise._handler(x).visit(resolver, fulfill, reject, resolver.notify); |
| } |
| |
| return p; |
| |
| function fulfill(x) { |
| /*jshint validthis:true*/ |
| if(this.resolved) { // this === resolver |
| return; |
| } |
| |
| results.push(x); |
| if(--nFulfill === 0) { |
| errors = null; |
| this.resolve(results); |
| } |
| } |
| |
| function reject(e) { |
| /*jshint validthis:true*/ |
| if(this.resolved) { // this === resolver |
| return; |
| } |
| |
| errors.push(e); |
| if(--nReject === 0) { |
| results = null; |
| this.reject(errors); |
| } |
| } |
| } |
| |
| /** |
| * Apply f to the value of each promise in a list of promises |
| * and return a new list containing the results. |
| * @param {array} promises |
| * @param {function(x:*, index:Number):*} f mapping function |
| * @returns {Promise} |
| */ |
| function map(promises, f) { |
| return Promise._traverse(f, promises); |
| } |
| |
| /** |
| * Filter the provided array of promises using the provided predicate. Input may |
| * contain promises and values |
| * @param {Array} promises array of promises and values |
| * @param {function(x:*, index:Number):boolean} predicate filtering predicate. |
| * Must return truthy (or promise for truthy) for items to retain. |
| * @returns {Promise} promise that will fulfill with an array containing all items |
| * for which predicate returned truthy. |
| */ |
| function filter(promises, predicate) { |
| var a = slice.call(promises); |
| return Promise._traverse(predicate, a).then(function(keep) { |
| return filterSync(a, keep); |
| }); |
| } |
| |
| function filterSync(promises, keep) { |
| // Safe because we know all promises have fulfilled if we've made it this far |
| var l = keep.length; |
| var filtered = new Array(l); |
| for(var i=0, j=0; i<l; ++i) { |
| if(keep[i]) { |
| filtered[j++] = Promise._handler(promises[i]).value; |
| } |
| } |
| filtered.length = j; |
| return filtered; |
| |
| } |
| |
| /** |
| * Return a promise that will always fulfill with an array containing |
| * the outcome states of all input promises. The returned promise |
| * will never reject. |
| * @param {Array} promises |
| * @returns {Promise} promise for array of settled state descriptors |
| */ |
| function settle(promises) { |
| return all(promises.map(settleOne)); |
| } |
| |
| function settleOne(p) { |
| // Optimize the case where we get an already-resolved when.js promise |
| // by extracting its state: |
| var handler; |
| if (p instanceof Promise) { |
| // This is our own Promise type and we can reach its handler internals: |
| handler = p._handler.join(); |
| } |
| if((handler && handler.state() === 0) || !handler) { |
| // Either still pending, or not a Promise at all: |
| return toPromise(p).then(state.fulfilled, state.rejected); |
| } |
| |
| // The promise is our own, but it is already resolved. Take a shortcut. |
| // Since we're not actually handling the resolution, we need to disable |
| // rejection reporting. |
| handler._unreport(); |
| return state.inspect(handler); |
| } |
| |
| /** |
| * 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} promises array or promise for an array of anything, |
| * may contain a mix of promises and values. |
| * @param {function(accumulated:*, x:*, index:Number):*} f reduce function |
| * @returns {Promise} that will resolve to the final reduced value |
| */ |
| function reduce(promises, f /*, initialValue */) { |
| return arguments.length > 2 ? ar.call(promises, liftCombine(f), arguments[2]) |
| : ar.call(promises, liftCombine(f)); |
| } |
| |
| /** |
| * Traditional reduce function, similar to `Array.prototype.reduceRight()`, 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} promises array or promise for an array of anything, |
| * may contain a mix of promises and values. |
| * @param {function(accumulated:*, x:*, index:Number):*} f reduce function |
| * @returns {Promise} that will resolve to the final reduced value |
| */ |
| function reduceRight(promises, f /*, initialValue */) { |
| return arguments.length > 2 ? arr.call(promises, liftCombine(f), arguments[2]) |
| : arr.call(promises, liftCombine(f)); |
| } |
| |
| function liftCombine(f) { |
| return function(z, x, i) { |
| return applyFold(f, void 0, [z,x,i]); |
| }; |
| } |
| }; |
| |
| }); |
| }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); |