UNPKG

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