| import { |
| isFunction, |
| now |
| } from './utils'; |
| |
| import { |
| noop, |
| subscribe, |
| initializePromise, |
| invokeCallback, |
| FULFILLED, |
| REJECTED |
| } from './-internal'; |
| |
| import asap from './asap'; |
| |
| import all from './promise/all'; |
| import race from './promise/race'; |
| import Resolve from './promise/resolve'; |
| import Reject from './promise/reject'; |
| |
| var counter = 0; |
| |
| function needsResolver() { |
| throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); |
| } |
| |
| function needsNew() { |
| throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); |
| } |
| |
| export default Promise; |
| /** |
| Promise objects represent the eventual result of an asynchronous operation. The |
| primary way of interacting with a promise is through its `then` method, which |
| registers callbacks to receive either a promise’s eventual value or the reason |
| why the promise cannot be fulfilled. |
| |
| Terminology |
| ----------- |
| |
| - `promise` is an object or function with a `then` method whose behavior conforms to this specification. |
| - `thenable` is an object or function that defines a `then` method. |
| - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). |
| - `exception` is a value that is thrown using the throw statement. |
| - `reason` is a value that indicates why a promise was rejected. |
| - `settled` the final resting state of a promise, fulfilled or rejected. |
| |
| A promise can be in one of three states: pending, fulfilled, or rejected. |
| |
| Promises that are fulfilled have a fulfillment value and are in the fulfilled |
| state. Promises that are rejected have a rejection reason and are in the |
| rejected state. A fulfillment value is never a thenable. |
| |
| Promises can also be said to *resolve* a value. If this value is also a |
| promise, then the original promise's settled state will match the value's |
| settled state. So a promise that *resolves* a promise that rejects will |
| itself reject, and a promise that *resolves* a promise that fulfills will |
| itself fulfill. |
| |
| |
| Basic Usage: |
| ------------ |
| |
| ```js |
| var promise = new Promise(function(resolve, reject) { |
| // on success |
| resolve(value); |
| |
| // on failure |
| reject(reason); |
| }); |
| |
| promise.then(function(value) { |
| // on fulfillment |
| }, function(reason) { |
| // on rejection |
| }); |
| ``` |
| |
| Advanced Usage: |
| --------------- |
| |
| Promises shine when abstracting away asynchronous interactions such as |
| `XMLHttpRequest`s. |
| |
| ```js |
| function getJSON(url) { |
| return new Promise(function(resolve, reject){ |
| var xhr = new XMLHttpRequest(); |
| |
| xhr.open('GET', url); |
| xhr.onreadystatechange = handler; |
| xhr.responseType = 'json'; |
| xhr.setRequestHeader('Accept', 'application/json'); |
| xhr.send(); |
| |
| function handler() { |
| if (this.readyState === this.DONE) { |
| if (this.status === 200) { |
| resolve(this.response); |
| } else { |
| reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); |
| } |
| } |
| }; |
| }); |
| } |
| |
| getJSON('/posts.json').then(function(json) { |
| // on fulfillment |
| }, function(reason) { |
| // on rejection |
| }); |
| ``` |
| |
| Unlike callbacks, promises are great composable primitives. |
| |
| ```js |
| Promise.all([ |
| getJSON('/posts'), |
| getJSON('/comments') |
| ]).then(function(values){ |
| values[0] // => postsJSON |
| values[1] // => commentsJSON |
| |
| return values; |
| }); |
| ``` |
| |
| @class Promise |
| @param {function} resolver |
| Useful for tooling. |
| @constructor |
| */ |
| function Promise(resolver) { |
| this._id = counter++; |
| this._state = undefined; |
| this._result = undefined; |
| this._subscribers = []; |
| |
| if (noop !== resolver) { |
| if (!isFunction(resolver)) { |
| needsResolver(); |
| } |
| |
| if (!(this instanceof Promise)) { |
| needsNew(); |
| } |
| |
| initializePromise(this, resolver); |
| } |
| } |
| |
| Promise.all = all; |
| Promise.race = race; |
| Promise.resolve = Resolve; |
| Promise.reject = Reject; |
| |
| Promise.prototype = { |
| constructor: Promise, |
| |
| /** |
| The primary way of interacting with a promise is through its `then` method, |
| which registers callbacks to receive either a promise's eventual value or the |
| reason why the promise cannot be fulfilled. |
| |
| ```js |
| findUser().then(function(user){ |
| // user is available |
| }, function(reason){ |
| // user is unavailable, and you are given the reason why |
| }); |
| ``` |
| |
| Chaining |
| -------- |
| |
| The return value of `then` is itself a promise. This second, 'downstream' |
| promise is resolved with the return value of the first promise's fulfillment |
| or rejection handler, or rejected if the handler throws an exception. |
| |
| ```js |
| findUser().then(function (user) { |
| return user.name; |
| }, function (reason) { |
| return 'default name'; |
| }).then(function (userName) { |
| // If `findUser` fulfilled, `userName` will be the user's name, otherwise it |
| // will be `'default name'` |
| }); |
| |
| findUser().then(function (user) { |
| throw new Error('Found user, but still unhappy'); |
| }, function (reason) { |
| throw new Error('`findUser` rejected and we're unhappy'); |
| }).then(function (value) { |
| // never reached |
| }, function (reason) { |
| // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. |
| // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. |
| }); |
| ``` |
| If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. |
| |
| ```js |
| findUser().then(function (user) { |
| throw new PedagogicalException('Upstream error'); |
| }).then(function (value) { |
| // never reached |
| }).then(function (value) { |
| // never reached |
| }, function (reason) { |
| // The `PedgagocialException` is propagated all the way down to here |
| }); |
| ``` |
| |
| Assimilation |
| ------------ |
| |
| Sometimes the value you want to propagate to a downstream promise can only be |
| retrieved asynchronously. This can be achieved by returning a promise in the |
| fulfillment or rejection handler. The downstream promise will then be pending |
| until the returned promise is settled. This is called *assimilation*. |
| |
| ```js |
| findUser().then(function (user) { |
| return findCommentsByAuthor(user); |
| }).then(function (comments) { |
| // The user's comments are now available |
| }); |
| ``` |
| |
| If the assimliated promise rejects, then the downstream promise will also reject. |
| |
| ```js |
| findUser().then(function (user) { |
| return findCommentsByAuthor(user); |
| }).then(function (comments) { |
| // If `findCommentsByAuthor` fulfills, we'll have the value here |
| }, function (reason) { |
| // If `findCommentsByAuthor` rejects, we'll have the reason here |
| }); |
| ``` |
| |
| Simple Example |
| -------------- |
| |
| Synchronous Example |
| |
| ```javascript |
| var result; |
| |
| try { |
| result = findResult(); |
| // success |
| } catch(reason) { |
| // failure |
| } |
| ``` |
| |
| Errback Example |
| |
| ```js |
| findResult(function(result, err){ |
| if (err) { |
| // failure |
| } else { |
| // success |
| } |
| }); |
| ``` |
| |
| Promise Example; |
| |
| ```javascript |
| findResult().then(function(result){ |
| // success |
| }, function(reason){ |
| // failure |
| }); |
| ``` |
| |
| Advanced Example |
| -------------- |
| |
| Synchronous Example |
| |
| ```javascript |
| var author, books; |
| |
| try { |
| author = findAuthor(); |
| books = findBooksByAuthor(author); |
| // success |
| } catch(reason) { |
| // failure |
| } |
| ``` |
| |
| Errback Example |
| |
| ```js |
| |
| function foundBooks(books) { |
| |
| } |
| |
| function failure(reason) { |
| |
| } |
| |
| findAuthor(function(author, err){ |
| if (err) { |
| failure(err); |
| // failure |
| } else { |
| try { |
| findBoooksByAuthor(author, function(books, err) { |
| if (err) { |
| failure(err); |
| } else { |
| try { |
| foundBooks(books); |
| } catch(reason) { |
| failure(reason); |
| } |
| } |
| }); |
| } catch(error) { |
| failure(err); |
| } |
| // success |
| } |
| }); |
| ``` |
| |
| Promise Example; |
| |
| ```javascript |
| findAuthor(). |
| then(findBooksByAuthor). |
| then(function(books){ |
| // found books |
| }).catch(function(reason){ |
| // something went wrong |
| }); |
| ``` |
| |
| @method then |
| @param {Function} onFulfilled |
| @param {Function} onRejected |
| Useful for tooling. |
| @return {Promise} |
| */ |
| then: function(onFulfillment, onRejection) { |
| var parent = this; |
| var state = parent._state; |
| |
| if (state === FULFILLED && !onFulfillment || state === REJECTED && !onRejection) { |
| return this; |
| } |
| |
| var child = new this.constructor(noop); |
| var result = parent._result; |
| |
| if (state) { |
| var callback = arguments[state - 1]; |
| asap(function(){ |
| invokeCallback(state, child, callback, result); |
| }); |
| } else { |
| subscribe(parent, child, onFulfillment, onRejection); |
| } |
| |
| return child; |
| }, |
| |
| /** |
| `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same |
| as the catch block of a try/catch statement. |
| |
| ```js |
| function findAuthor(){ |
| throw new Error('couldn't find that author'); |
| } |
| |
| // synchronous |
| try { |
| findAuthor(); |
| } catch(reason) { |
| // something went wrong |
| } |
| |
| // async with promises |
| findAuthor().catch(function(reason){ |
| // something went wrong |
| }); |
| ``` |
| |
| @method catch |
| @param {Function} onRejection |
| Useful for tooling. |
| @return {Promise} |
| */ |
| 'catch': function(onRejection) { |
| return this.then(null, onRejection); |
| } |
| }; |