d95951e2c1900c5ba7dfa49d5a10b4b0f44fd721
[aai/esr-gui.git] /
1 import {
2   isFunction
3 } from './utils';
4
5 import {
6   noop,
7   nextId,
8   PROMISE_ID,
9   initializePromise
10 } from './-internal';
11
12 import {
13   asap,
14   setAsap,
15   setScheduler
16 } from './asap';
17
18 import all from './promise/all';
19 import race from './promise/race';
20 import Resolve from './promise/resolve';
21 import Reject from './promise/reject';
22 import then from './then';
23
24
25 function needsResolver() {
26   throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
27 }
28
29 function needsNew() {
30   throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
31 }
32
33 export default Promise;
34 /**
35   Promise objects represent the eventual result of an asynchronous operation. The
36   primary way of interacting with a promise is through its `then` method, which
37   registers callbacks to receive either a promise's eventual value or the reason
38   why the promise cannot be fulfilled.
39
40   Terminology
41   -----------
42
43   - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
44   - `thenable` is an object or function that defines a `then` method.
45   - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
46   - `exception` is a value that is thrown using the throw statement.
47   - `reason` is a value that indicates why a promise was rejected.
48   - `settled` the final resting state of a promise, fulfilled or rejected.
49
50   A promise can be in one of three states: pending, fulfilled, or rejected.
51
52   Promises that are fulfilled have a fulfillment value and are in the fulfilled
53   state.  Promises that are rejected have a rejection reason and are in the
54   rejected state.  A fulfillment value is never a thenable.
55
56   Promises can also be said to *resolve* a value.  If this value is also a
57   promise, then the original promise's settled state will match the value's
58   settled state.  So a promise that *resolves* a promise that rejects will
59   itself reject, and a promise that *resolves* a promise that fulfills will
60   itself fulfill.
61
62
63   Basic Usage:
64   ------------
65
66   ```js
67   var promise = new Promise(function(resolve, reject) {
68     // on success
69     resolve(value);
70
71     // on failure
72     reject(reason);
73   });
74
75   promise.then(function(value) {
76     // on fulfillment
77   }, function(reason) {
78     // on rejection
79   });
80   ```
81
82   Advanced Usage:
83   ---------------
84
85   Promises shine when abstracting away asynchronous interactions such as
86   `XMLHttpRequest`s.
87
88   ```js
89   function getJSON(url) {
90     return new Promise(function(resolve, reject){
91       var xhr = new XMLHttpRequest();
92
93       xhr.open('GET', url);
94       xhr.onreadystatechange = handler;
95       xhr.responseType = 'json';
96       xhr.setRequestHeader('Accept', 'application/json');
97       xhr.send();
98
99       function handler() {
100         if (this.readyState === this.DONE) {
101           if (this.status === 200) {
102             resolve(this.response);
103           } else {
104             reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
105           }
106         }
107       };
108     });
109   }
110
111   getJSON('/posts.json').then(function(json) {
112     // on fulfillment
113   }, function(reason) {
114     // on rejection
115   });
116   ```
117
118   Unlike callbacks, promises are great composable primitives.
119
120   ```js
121   Promise.all([
122     getJSON('/posts'),
123     getJSON('/comments')
124   ]).then(function(values){
125     values[0] // => postsJSON
126     values[1] // => commentsJSON
127
128     return values;
129   });
130   ```
131
132   @class Promise
133   @param {function} resolver
134   Useful for tooling.
135   @constructor
136 */
137 function Promise(resolver) {
138   this[PROMISE_ID] = nextId();
139   this._result = this._state = undefined;
140   this._subscribers = [];
141
142   if (noop !== resolver) {
143     typeof resolver !== 'function' && needsResolver();
144     this instanceof Promise ? initializePromise(this, resolver) : needsNew();
145   }
146 }
147
148 Promise.all = all;
149 Promise.race = race;
150 Promise.resolve = Resolve;
151 Promise.reject = Reject;
152 Promise._setScheduler = setScheduler;
153 Promise._setAsap = setAsap;
154 Promise._asap = asap;
155
156 Promise.prototype = {
157   constructor: Promise,
158
159 /**
160   The primary way of interacting with a promise is through its `then` method,
161   which registers callbacks to receive either a promise's eventual value or the
162   reason why the promise cannot be fulfilled.
163
164   ```js
165   findUser().then(function(user){
166     // user is available
167   }, function(reason){
168     // user is unavailable, and you are given the reason why
169   });
170   ```
171
172   Chaining
173   --------
174
175   The return value of `then` is itself a promise.  This second, 'downstream'
176   promise is resolved with the return value of the first promise's fulfillment
177   or rejection handler, or rejected if the handler throws an exception.
178
179   ```js
180   findUser().then(function (user) {
181     return user.name;
182   }, function (reason) {
183     return 'default name';
184   }).then(function (userName) {
185     // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
186     // will be `'default name'`
187   });
188
189   findUser().then(function (user) {
190     throw new Error('Found user, but still unhappy');
191   }, function (reason) {
192     throw new Error('`findUser` rejected and we're unhappy');
193   }).then(function (value) {
194     // never reached
195   }, function (reason) {
196     // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
197     // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
198   });
199   ```
200   If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
201
202   ```js
203   findUser().then(function (user) {
204     throw new PedagogicalException('Upstream error');
205   }).then(function (value) {
206     // never reached
207   }).then(function (value) {
208     // never reached
209   }, function (reason) {
210     // The `PedgagocialException` is propagated all the way down to here
211   });
212   ```
213
214   Assimilation
215   ------------
216
217   Sometimes the value you want to propagate to a downstream promise can only be
218   retrieved asynchronously. This can be achieved by returning a promise in the
219   fulfillment or rejection handler. The downstream promise will then be pending
220   until the returned promise is settled. This is called *assimilation*.
221
222   ```js
223   findUser().then(function (user) {
224     return findCommentsByAuthor(user);
225   }).then(function (comments) {
226     // The user's comments are now available
227   });
228   ```
229
230   If the assimliated promise rejects, then the downstream promise will also reject.
231
232   ```js
233   findUser().then(function (user) {
234     return findCommentsByAuthor(user);
235   }).then(function (comments) {
236     // If `findCommentsByAuthor` fulfills, we'll have the value here
237   }, function (reason) {
238     // If `findCommentsByAuthor` rejects, we'll have the reason here
239   });
240   ```
241
242   Simple Example
243   --------------
244
245   Synchronous Example
246
247   ```javascript
248   var result;
249
250   try {
251     result = findResult();
252     // success
253   } catch(reason) {
254     // failure
255   }
256   ```
257
258   Errback Example
259
260   ```js
261   findResult(function(result, err){
262     if (err) {
263       // failure
264     } else {
265       // success
266     }
267   });
268   ```
269
270   Promise Example;
271
272   ```javascript
273   findResult().then(function(result){
274     // success
275   }, function(reason){
276     // failure
277   });
278   ```
279
280   Advanced Example
281   --------------
282
283   Synchronous Example
284
285   ```javascript
286   var author, books;
287
288   try {
289     author = findAuthor();
290     books  = findBooksByAuthor(author);
291     // success
292   } catch(reason) {
293     // failure
294   }
295   ```
296
297   Errback Example
298
299   ```js
300
301   function foundBooks(books) {
302
303   }
304
305   function failure(reason) {
306
307   }
308
309   findAuthor(function(author, err){
310     if (err) {
311       failure(err);
312       // failure
313     } else {
314       try {
315         findBoooksByAuthor(author, function(books, err) {
316           if (err) {
317             failure(err);
318           } else {
319             try {
320               foundBooks(books);
321             } catch(reason) {
322               failure(reason);
323             }
324           }
325         });
326       } catch(error) {
327         failure(err);
328       }
329       // success
330     }
331   });
332   ```
333
334   Promise Example;
335
336   ```javascript
337   findAuthor().
338     then(findBooksByAuthor).
339     then(function(books){
340       // found books
341   }).catch(function(reason){
342     // something went wrong
343   });
344   ```
345
346   @method then
347   @param {Function} onFulfilled
348   @param {Function} onRejected
349   Useful for tooling.
350   @return {Promise}
351 */
352   then: then,
353
354 /**
355   `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
356   as the catch block of a try/catch statement.
357
358   ```js
359   function findAuthor(){
360     throw new Error('couldn't find that author');
361   }
362
363   // synchronous
364   try {
365     findAuthor();
366   } catch(reason) {
367     // something went wrong
368   }
369
370   // async with promises
371   findAuthor().catch(function(reason){
372     // something went wrong
373   });
374   ```
375
376   @method catch
377   @param {Function} onRejection
378   Useful for tooling.
379   @return {Promise}
380 */
381   'catch': function(onRejection) {
382     return this.then(null, onRejection);
383   }
384 };