1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */
|
2 |
|
3 | /**
|
4 | * Promises/A+ and when() implementation
|
5 | * when is part of the cujoJS family of libraries (http://cujojs.com/)
|
6 | * @author Brian Cavalier
|
7 | * @author John Hann
|
8 | * @version 3.4.2
|
9 | */
|
10 | (function(define) { 'use strict';
|
11 | define(function (require) {
|
12 |
|
13 | var timed = require('./lib/decorators/timed');
|
14 | var array = require('./lib/decorators/array');
|
15 | var flow = require('./lib/decorators/flow');
|
16 | var fold = require('./lib/decorators/fold');
|
17 | var inspect = require('./lib/decorators/inspect');
|
18 | var generate = require('./lib/decorators/iterate');
|
19 | var progress = require('./lib/decorators/progress');
|
20 | var withThis = require('./lib/decorators/with');
|
21 | var unhandledRejection = require('./lib/decorators/unhandledRejection');
|
22 | var TimeoutError = require('./lib/TimeoutError');
|
23 |
|
24 | var Promise = [array, flow, fold, generate, progress,
|
25 | inspect, withThis, timed, unhandledRejection]
|
26 | .reduce(function(Promise, feature) {
|
27 | return feature(Promise);
|
28 | }, require('./lib/Promise'));
|
29 |
|
30 | var slice = Array.prototype.slice;
|
31 |
|
32 | // Public API
|
33 |
|
34 | when.promise = promise; // Create a pending promise
|
35 | when.resolve = Promise.resolve; // Create a resolved promise
|
36 | when.reject = Promise.reject; // Create a rejected promise
|
37 |
|
38 | when.lift = lift; // lift a function to return promises
|
39 | when['try'] = attempt; // call a function and return a promise
|
40 | when.attempt = attempt; // alias for when.try
|
41 |
|
42 | when.iterate = Promise.iterate; // Generate a stream of promises
|
43 | when.unfold = Promise.unfold; // Generate a stream of promises
|
44 |
|
45 | when.join = join; // Join 2 or more promises
|
46 |
|
47 | when.all = all; // Resolve a list of promises
|
48 | when.settle = settle; // Settle a list of promises
|
49 |
|
50 | when.any = lift(Promise.any); // One-winner race
|
51 | when.some = lift(Promise.some); // Multi-winner race
|
52 | when.race = lift(Promise.race); // First-to-settle race
|
53 |
|
54 | when.map = map; // Array.map() for promises
|
55 | when.filter = filter; // Array.filter() for promises
|
56 | when.reduce = reduce; // Array.reduce() for promises
|
57 | when.reduceRight = reduceRight; // Array.reduceRight() for promises
|
58 |
|
59 | when.isPromiseLike = isPromiseLike; // Is something promise-like, aka thenable
|
60 |
|
61 | when.Promise = Promise; // Promise constructor
|
62 | when.defer = defer; // Create a {promise, resolve, reject} tuple
|
63 |
|
64 | // Error types
|
65 |
|
66 | when.TimeoutError = TimeoutError;
|
67 |
|
68 | /**
|
69 | * Get a trusted promise for x, or by transforming x with onFulfilled
|
70 | *
|
71 | * @param {*} x
|
72 | * @param {function?} onFulfilled callback to be called when x is
|
73 | * successfully fulfilled. If promiseOrValue is an immediate value, callback
|
74 | * will be invoked immediately.
|
75 | * @param {function?} onRejected callback to be called when x is
|
76 | * rejected.
|
77 | * @param {function?} onProgress callback to be called when progress updates
|
78 | * are issued for x. @deprecated
|
79 | * @returns {Promise} a new promise that will fulfill with the return
|
80 | * value of callback or errback or the completion value of promiseOrValue if
|
81 | * callback and/or errback is not supplied.
|
82 | */
|
83 | function when(x, onFulfilled, onRejected) {
|
84 | var p = Promise.resolve(x);
|
85 | if(arguments.length < 2) {
|
86 | return p;
|
87 | }
|
88 |
|
89 | return arguments.length > 3
|
90 | ? p.then(onFulfilled, onRejected, arguments[3])
|
91 | : p.then(onFulfilled, onRejected);
|
92 | }
|
93 |
|
94 | /**
|
95 | * Creates a new promise whose fate is determined by resolver.
|
96 | * @param {function} resolver function(resolve, reject, notify)
|
97 | * @returns {Promise} promise whose fate is determine by resolver
|
98 | */
|
99 | function promise(resolver) {
|
100 | return new Promise(resolver);
|
101 | }
|
102 |
|
103 | /**
|
104 | * Lift the supplied function, creating a version of f that returns
|
105 | * promises, and accepts promises as arguments.
|
106 | * @param {function} f
|
107 | * @returns {Function} version of f that returns promises
|
108 | */
|
109 | function lift(f) {
|
110 | return function() {
|
111 | return _apply(f, this, slice.call(arguments));
|
112 | };
|
113 | }
|
114 |
|
115 | /**
|
116 | * Call f in a future turn, with the supplied args, and return a promise
|
117 | * for the result.
|
118 | * @param {function} f
|
119 | * @returns {Promise}
|
120 | */
|
121 | function attempt(f /*, args... */) {
|
122 | /*jshint validthis:true */
|
123 | return _apply(f, this, slice.call(arguments, 1));
|
124 | }
|
125 |
|
126 | /**
|
127 | * try/lift helper that allows specifying thisArg
|
128 | * @private
|
129 | */
|
130 | function _apply(f, thisArg, args) {
|
131 | return Promise.all(args).then(function(args) {
|
132 | return f.apply(thisArg, args);
|
133 | });
|
134 | }
|
135 |
|
136 | /**
|
137 | * Creates a {promise, resolver} pair, either or both of which
|
138 | * may be given out safely to consumers.
|
139 | * @return {{promise: Promise, resolve: function, reject: function, notify: function}}
|
140 | */
|
141 | function defer() {
|
142 | return new Deferred();
|
143 | }
|
144 |
|
145 | function Deferred() {
|
146 | var p = Promise._defer();
|
147 |
|
148 | function resolve(x) { p._handler.resolve(x); }
|
149 | function reject(x) { p._handler.reject(x); }
|
150 | function notify(x) { p._handler.notify(x); }
|
151 |
|
152 | this.promise = p;
|
153 | this.resolve = resolve;
|
154 | this.reject = reject;
|
155 | this.notify = notify;
|
156 | this.resolver = { resolve: resolve, reject: reject, notify: notify };
|
157 | }
|
158 |
|
159 | /**
|
160 | * Determines if x is promise-like, i.e. a thenable object
|
161 | * NOTE: Will return true for *any thenable object*, and isn't truly
|
162 | * safe, since it may attempt to access the `then` property of x (i.e.
|
163 | * clever/malicious getters may do weird things)
|
164 | * @param {*} x anything
|
165 | * @returns {boolean} true if x is promise-like
|
166 | */
|
167 | function isPromiseLike(x) {
|
168 | return x && typeof x.then === 'function';
|
169 | }
|
170 |
|
171 | /**
|
172 | * Return a promise that will resolve only once all the supplied arguments
|
173 | * have resolved. The resolution value of the returned promise will be an array
|
174 | * containing the resolution values of each of the arguments.
|
175 | * @param {...*} arguments may be a mix of promises and values
|
176 | * @returns {Promise}
|
177 | */
|
178 | function join(/* ...promises */) {
|
179 | return Promise.all(arguments);
|
180 | }
|
181 |
|
182 | /**
|
183 | * Return a promise that will fulfill once all input promises have
|
184 | * fulfilled, or reject when any one input promise rejects.
|
185 | * @param {array|Promise} promises array (or promise for an array) of promises
|
186 | * @returns {Promise}
|
187 | */
|
188 | function all(promises) {
|
189 | return when(promises, Promise.all);
|
190 | }
|
191 |
|
192 | /**
|
193 | * Return a promise that will always fulfill with an array containing
|
194 | * the outcome states of all input promises. The returned promise
|
195 | * will only reject if `promises` itself is a rejected promise.
|
196 | * @param {array|Promise} promises array (or promise for an array) of promises
|
197 | * @returns {Promise} promise for array of settled state descriptors
|
198 | */
|
199 | function settle(promises) {
|
200 | return when(promises, Promise.settle);
|
201 | }
|
202 |
|
203 | /**
|
204 | * Promise-aware array map function, similar to `Array.prototype.map()`,
|
205 | * but input array may contain promises or values.
|
206 | * @param {Array|Promise} promises array of anything, may contain promises and values
|
207 | * @param {function(x:*, index:Number):*} mapFunc map function which may
|
208 | * return a promise or value
|
209 | * @returns {Promise} promise that will fulfill with an array of mapped values
|
210 | * or reject if any input promise rejects.
|
211 | */
|
212 | function map(promises, mapFunc) {
|
213 | return when(promises, function(promises) {
|
214 | return Promise.map(promises, mapFunc);
|
215 | });
|
216 | }
|
217 |
|
218 | /**
|
219 | * Filter the provided array of promises using the provided predicate. Input may
|
220 | * contain promises and values
|
221 | * @param {Array|Promise} promises array of promises and values
|
222 | * @param {function(x:*, index:Number):boolean} predicate filtering predicate.
|
223 | * Must return truthy (or promise for truthy) for items to retain.
|
224 | * @returns {Promise} promise that will fulfill with an array containing all items
|
225 | * for which predicate returned truthy.
|
226 | */
|
227 | function filter(promises, predicate) {
|
228 | return when(promises, function(promises) {
|
229 | return Promise.filter(promises, predicate);
|
230 | });
|
231 | }
|
232 |
|
233 | /**
|
234 | * Traditional reduce function, similar to `Array.prototype.reduce()`, but
|
235 | * input may contain promises and/or values, and reduceFunc
|
236 | * may return either a value or a promise, *and* initialValue may
|
237 | * be a promise for the starting value.
|
238 | * @param {Array|Promise} promises array or promise for an array of anything,
|
239 | * may contain a mix of promises and values.
|
240 | * @param {function(accumulated:*, x:*, index:Number):*} f reduce function
|
241 | * @returns {Promise} that will resolve to the final reduced value
|
242 | */
|
243 | function reduce(promises, f /*, initialValue */) {
|
244 | /*jshint unused:false*/
|
245 | var args = slice.call(arguments, 1);
|
246 | return when(promises, function(array) {
|
247 | args.unshift(array);
|
248 | return Promise.reduce.apply(Promise, args);
|
249 | });
|
250 | }
|
251 |
|
252 | /**
|
253 | * Traditional reduce function, similar to `Array.prototype.reduceRight()`, but
|
254 | * input may contain promises and/or values, and reduceFunc
|
255 | * may return either a value or a promise, *and* initialValue may
|
256 | * be a promise for the starting value.
|
257 | * @param {Array|Promise} promises array or promise for an array of anything,
|
258 | * may contain a mix of promises and values.
|
259 | * @param {function(accumulated:*, x:*, index:Number):*} f reduce function
|
260 | * @returns {Promise} that will resolve to the final reduced value
|
261 | */
|
262 | function reduceRight(promises, f /*, initialValue */) {
|
263 | /*jshint unused:false*/
|
264 | var args = slice.call(arguments, 1);
|
265 | return when(promises, function(array) {
|
266 | args.unshift(array);
|
267 | return Promise.reduceRight.apply(Promise, args);
|
268 | });
|
269 | }
|
270 |
|
271 | return when;
|
272 | });
|
273 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); });
|