1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */
|
2 | /** @author Brian Cavalier */
|
3 | /** @author John Hann */
|
4 |
|
5 | (function(define) { 'use strict';
|
6 | define(function(require) {
|
7 |
|
8 | var state = require('../state');
|
9 | var applier = require('../apply');
|
10 |
|
11 | return function array(Promise) {
|
12 |
|
13 | var applyFold = applier(Promise);
|
14 | var toPromise = Promise.resolve;
|
15 | var all = Promise.all;
|
16 |
|
17 | var ar = Array.prototype.reduce;
|
18 | var arr = Array.prototype.reduceRight;
|
19 | var slice = Array.prototype.slice;
|
20 |
|
21 | // Additional array combinators
|
22 |
|
23 | Promise.any = any;
|
24 | Promise.some = some;
|
25 | Promise.settle = settle;
|
26 |
|
27 | Promise.map = map;
|
28 | Promise.filter = filter;
|
29 | Promise.reduce = reduce;
|
30 | Promise.reduceRight = reduceRight;
|
31 |
|
32 | /**
|
33 | * When this promise fulfills with an array, do
|
34 | * onFulfilled.apply(void 0, array)
|
35 | * @param {function} onFulfilled function to apply
|
36 | * @returns {Promise} promise for the result of applying onFulfilled
|
37 | */
|
38 | Promise.prototype.spread = function(onFulfilled) {
|
39 | return this.then(all).then(function(array) {
|
40 | return onFulfilled.apply(this, array);
|
41 | });
|
42 | };
|
43 |
|
44 | return Promise;
|
45 |
|
46 | /**
|
47 | * One-winner competitive race.
|
48 | * Return a promise that will fulfill when one of the promises
|
49 | * in the input array fulfills, or will reject when all promises
|
50 | * have rejected.
|
51 | * @param {array} promises
|
52 | * @returns {Promise} promise for the first fulfilled value
|
53 | */
|
54 | function any(promises) {
|
55 | var p = Promise._defer();
|
56 | var resolver = p._handler;
|
57 | var l = promises.length>>>0;
|
58 |
|
59 | var pending = l;
|
60 | var errors = [];
|
61 |
|
62 | for (var h, x, i = 0; i < l; ++i) {
|
63 | x = promises[i];
|
64 | if(x === void 0 && !(i in promises)) {
|
65 | --pending;
|
66 | continue;
|
67 | }
|
68 |
|
69 | h = Promise._handler(x);
|
70 | if(h.state() > 0) {
|
71 | resolver.become(h);
|
72 | Promise._visitRemaining(promises, i, h);
|
73 | break;
|
74 | } else {
|
75 | h.visit(resolver, handleFulfill, handleReject);
|
76 | }
|
77 | }
|
78 |
|
79 | if(pending === 0) {
|
80 | resolver.reject(new RangeError('any(): array must not be empty'));
|
81 | }
|
82 |
|
83 | return p;
|
84 |
|
85 | function handleFulfill(x) {
|
86 | /*jshint validthis:true*/
|
87 | errors = null;
|
88 | this.resolve(x); // this === resolver
|
89 | }
|
90 |
|
91 | function handleReject(e) {
|
92 | /*jshint validthis:true*/
|
93 | if(this.resolved) { // this === resolver
|
94 | return;
|
95 | }
|
96 |
|
97 | errors.push(e);
|
98 | if(--pending === 0) {
|
99 | this.reject(errors);
|
100 | }
|
101 | }
|
102 | }
|
103 |
|
104 | /**
|
105 | * N-winner competitive race
|
106 | * Return a promise that will fulfill when n input promises have
|
107 | * fulfilled, or will reject when it becomes impossible for n
|
108 | * input promises to fulfill (ie when promises.length - n + 1
|
109 | * have rejected)
|
110 | * @param {array} promises
|
111 | * @param {number} n
|
112 | * @returns {Promise} promise for the earliest n fulfillment values
|
113 | *
|
114 | * @deprecated
|
115 | */
|
116 | function some(promises, n) {
|
117 | /*jshint maxcomplexity:7*/
|
118 | var p = Promise._defer();
|
119 | var resolver = p._handler;
|
120 |
|
121 | var results = [];
|
122 | var errors = [];
|
123 |
|
124 | var l = promises.length>>>0;
|
125 | var nFulfill = 0;
|
126 | var nReject;
|
127 | var x, i; // reused in both for() loops
|
128 |
|
129 | // First pass: count actual array items
|
130 | for(i=0; i<l; ++i) {
|
131 | x = promises[i];
|
132 | if(x === void 0 && !(i in promises)) {
|
133 | continue;
|
134 | }
|
135 | ++nFulfill;
|
136 | }
|
137 |
|
138 | // Compute actual goals
|
139 | n = Math.max(n, 0);
|
140 | nReject = (nFulfill - n + 1);
|
141 | nFulfill = Math.min(n, nFulfill);
|
142 |
|
143 | if(n > nFulfill) {
|
144 | resolver.reject(new RangeError('some(): array must contain at least '
|
145 | + n + ' item(s), but had ' + nFulfill));
|
146 | } else if(nFulfill === 0) {
|
147 | resolver.resolve(results);
|
148 | }
|
149 |
|
150 | // Second pass: observe each array item, make progress toward goals
|
151 | for(i=0; i<l; ++i) {
|
152 | x = promises[i];
|
153 | if(x === void 0 && !(i in promises)) {
|
154 | continue;
|
155 | }
|
156 |
|
157 | Promise._handler(x).visit(resolver, fulfill, reject, resolver.notify);
|
158 | }
|
159 |
|
160 | return p;
|
161 |
|
162 | function fulfill(x) {
|
163 | /*jshint validthis:true*/
|
164 | if(this.resolved) { // this === resolver
|
165 | return;
|
166 | }
|
167 |
|
168 | results.push(x);
|
169 | if(--nFulfill === 0) {
|
170 | errors = null;
|
171 | this.resolve(results);
|
172 | }
|
173 | }
|
174 |
|
175 | function reject(e) {
|
176 | /*jshint validthis:true*/
|
177 | if(this.resolved) { // this === resolver
|
178 | return;
|
179 | }
|
180 |
|
181 | errors.push(e);
|
182 | if(--nReject === 0) {
|
183 | results = null;
|
184 | this.reject(errors);
|
185 | }
|
186 | }
|
187 | }
|
188 |
|
189 | /**
|
190 | * Apply f to the value of each promise in a list of promises
|
191 | * and return a new list containing the results.
|
192 | * @param {array} promises
|
193 | * @param {function(x:*, index:Number):*} f mapping function
|
194 | * @returns {Promise}
|
195 | */
|
196 | function map(promises, f) {
|
197 | return Promise._traverse(f, promises);
|
198 | }
|
199 |
|
200 | /**
|
201 | * Filter the provided array of promises using the provided predicate. Input may
|
202 | * contain promises and values
|
203 | * @param {Array} promises array of promises and values
|
204 | * @param {function(x:*, index:Number):boolean} predicate filtering predicate.
|
205 | * Must return truthy (or promise for truthy) for items to retain.
|
206 | * @returns {Promise} promise that will fulfill with an array containing all items
|
207 | * for which predicate returned truthy.
|
208 | */
|
209 | function filter(promises, predicate) {
|
210 | var a = slice.call(promises);
|
211 | return Promise._traverse(predicate, a).then(function(keep) {
|
212 | return filterSync(a, keep);
|
213 | });
|
214 | }
|
215 |
|
216 | function filterSync(promises, keep) {
|
217 | // Safe because we know all promises have fulfilled if we've made it this far
|
218 | var l = keep.length;
|
219 | var filtered = new Array(l);
|
220 | for(var i=0, j=0; i<l; ++i) {
|
221 | if(keep[i]) {
|
222 | filtered[j++] = Promise._handler(promises[i]).value;
|
223 | }
|
224 | }
|
225 | filtered.length = j;
|
226 | return filtered;
|
227 |
|
228 | }
|
229 |
|
230 | /**
|
231 | * Return a promise that will always fulfill with an array containing
|
232 | * the outcome states of all input promises. The returned promise
|
233 | * will never reject.
|
234 | * @param {Array} promises
|
235 | * @returns {Promise} promise for array of settled state descriptors
|
236 | */
|
237 | function settle(promises) {
|
238 | return all(promises.map(settleOne));
|
239 | }
|
240 |
|
241 | function settleOne(p) {
|
242 | // Optimize the case where we get an already-resolved when.js promise
|
243 | // by extracting its state:
|
244 | var handler;
|
245 | if (p instanceof Promise) {
|
246 | // This is our own Promise type and we can reach its handler internals:
|
247 | handler = p._handler.join();
|
248 | }
|
249 | if((handler && handler.state() === 0) || !handler) {
|
250 | // Either still pending, or not a Promise at all:
|
251 | return toPromise(p).then(state.fulfilled, state.rejected);
|
252 | }
|
253 |
|
254 | // The promise is our own, but it is already resolved. Take a shortcut.
|
255 | // Since we're not actually handling the resolution, we need to disable
|
256 | // rejection reporting.
|
257 | handler._unreport();
|
258 | return state.inspect(handler);
|
259 | }
|
260 |
|
261 | /**
|
262 | * Traditional reduce function, similar to `Array.prototype.reduce()`, but
|
263 | * input may contain promises and/or values, and reduceFunc
|
264 | * may return either a value or a promise, *and* initialValue may
|
265 | * be a promise for the starting value.
|
266 | * @param {Array|Promise} promises array or promise for an array of anything,
|
267 | * may contain a mix of promises and values.
|
268 | * @param {function(accumulated:*, x:*, index:Number):*} f reduce function
|
269 | * @returns {Promise} that will resolve to the final reduced value
|
270 | */
|
271 | function reduce(promises, f /*, initialValue */) {
|
272 | return arguments.length > 2 ? ar.call(promises, liftCombine(f), arguments[2])
|
273 | : ar.call(promises, liftCombine(f));
|
274 | }
|
275 |
|
276 | /**
|
277 | * Traditional reduce function, similar to `Array.prototype.reduceRight()`, but
|
278 | * input may contain promises and/or values, and reduceFunc
|
279 | * may return either a value or a promise, *and* initialValue may
|
280 | * be a promise for the starting value.
|
281 | * @param {Array|Promise} promises array or promise for an array of anything,
|
282 | * may contain a mix of promises and values.
|
283 | * @param {function(accumulated:*, x:*, index:Number):*} f reduce function
|
284 | * @returns {Promise} that will resolve to the final reduced value
|
285 | */
|
286 | function reduceRight(promises, f /*, initialValue */) {
|
287 | return arguments.length > 2 ? arr.call(promises, liftCombine(f), arguments[2])
|
288 | : arr.call(promises, liftCombine(f));
|
289 | }
|
290 |
|
291 | function liftCombine(f) {
|
292 | return function(z, x, i) {
|
293 | return applyFold(f, void 0, [z,x,i]);
|
294 | };
|
295 | }
|
296 | };
|
297 |
|
298 | });
|
299 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
|