diff options
| -rw-r--r-- | src/Deferred.js | 260 | 
1 files changed, 137 insertions, 123 deletions
| diff --git a/src/Deferred.js b/src/Deferred.js index 76da90bd..f0c030b0 100644 --- a/src/Deferred.js +++ b/src/Deferred.js @@ -1,27 +1,148 @@  'use strict';  /** - * Constructs a promise manager. + * @ngdoc service + * @name angular.module.ng.$q + * @requires $rootScope   * - * @param {function(function)=} nextTick Function for executing functions in the next turn. Falls - *    back to `setTimeout` if undefined. - * @param {function(...*)=} exceptionHandler Function into which unexpected exceptions are passed for - *     debugging purposes. Falls back to `console.error` if undefined, - * @returns {object} Promise manager. + * @description + * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). + * + * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an + * interface for interacting with an object that represents the result of an action that is + * performed asynchronously, and may or may not be finished at any given point in time. + * + * From the perspective of dealing with error handling, deferred and promise apis are to + * asynchronous programing what `try`, `catch` and `throw` keywords are to synchronous programing. + * + * <pre> + *   // for the purpose of this example let's assume that variables `$q` and `scope` are + *   // available in the current lexical scope (they could have been injected or passed in). + * + *   function asyncGreet(name) { + *     var deferred = $q.defer(); + * + *     setTimeout(function() { + *       // since this fn executes async in a future turn of the event loop, we need to wrap + *       // our code into an $apply call so that the model changes are properly observed. + *       scope.$apply(function() { + *         if (okToGreet(name)) { + *           deferred.resolve('Hello, ' + name + '!'); + *         } else { + *           deferred.reject('Greeting ' + name + ' is not allowed.'); + *         } + *       }); + *     }, 1000); + * + *     return deferred.promise; + *   } + * + *   var promise = asyncGreet('Robin Hood'); + *   promise.then(function(greeting) { + *     alert('Success: ' + greeting); + *   }, function(reason) { + *     alert('Failed: ' + reason); + *   ); + * </pre> + * + * At first it might not be obvious why this extra complexity is worth the trouble. The payoff + * comes in the way of + * [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md). + * + * Additionally the promise api allows for composition that is very hard to do with the + * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. + * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the + * section on serial or parallel joining of promises. + * + * + * # The Deferred API + * + * A new instance of deferred is constructed by calling `$q.defer()`. + * + * The purpose of the deferred object is to expose the associated Promise instance as well as apis + * that can be used for signaling the successful or unsuccessful completion of the task. + * + * **Methods** + * + * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection + *   constructed via `$q.reject`, the promise will be rejected instead. + * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to + *   resolving it with a rejection constructed via `$q.reject`. + * + * **Properties** + * + * - promise – `{Promise}` – promise object associated with this deferred. + * + * + * # The Promise API + * + * A new promise instance is created when a deferred instance is created and can be retrieved by + * calling `deferred.promise`. + * + * The purpose of the promise object is to allow for interested parties to get access to the result + * of the deferred task when it completes. + * + * **Methods** + * + * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved + *   or rejected calls one of the success or error callbacks asynchronously as soon as the result + *   is available. The callbacks are called with a single argument the result or rejection reason. + * + *   This method *returns a new promise* which is resolved or rejected via the return value of the + *   `successCallback` or `errorCallback`. + * + * + * # Chaining promises + * + * Because calling `then` api of a promise returns a new derived promise, it is easily possible + * to create a chain of promises: + * + * <pre> + *   promiseB = promiseA.then(function(result) { + *     return result + 1; + *   }); + * + *   // promiseB will be resolved immediately after promiseA is resolved and it's value will be + *   // the result of promiseA incremented by 1 + * </pre> + * + * It is possible to create chains of any length and since a promise can be resolved with another + * promise (which will defer its resolution further), it is possible to pause/defer resolution of + * the promises at any point in the chain. This makes it possible to implement powerful apis like + * $http's response interceptors. + * + * + * # Differences between Kris Kowal's Q and $q + * + *  There are three main differences: + * + * - $q is integrated with the {@link angular.module.ng.$rootScope.Scope} Scope model observation + *   mechanism in angular, which means faster propagation of resolution or rejection into your + *   models and avoiding unnecessary browser repaints, which would result in flickering UI. + * - $q promises are recognized by the templating engine in angular, which means that in templates + *   you can treat promises attached to a scope as if they were the resulting values. + * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains + *   all the important functionality needed for common async tasks.   */ -function qFactory(nextTick, exceptionHandler) { +function $QProvider() { -  nextTick = nextTick || function(callback) { -    setTimeout(callback, 0); // very rare since most of queueing will be handled within $apply -  }; +  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { +    return qFactory(function(callback) { +      $rootScope.$evalAsync(callback); +    }, $exceptionHandler); +  }]; +} -  exceptionHandler = exceptionHandler || function(e) { -    // TODO(i): console.error is somehow reset to function(a) {}, it might be a JSTD bug -    if (console && console.log) { -      console.log(e); -    } -  } +/** + * Constructs a promise manager. + * + * @param {function(function)} nextTick Function for executing functions in the next turn. + * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for + *     debugging purposes. + * @returns {object} Promise manager. + */ +function qFactory(nextTick, exceptionHandler) {    /**     * @ngdoc @@ -264,110 +385,3 @@ function qFactory(nextTick, exceptionHandler) {      all: all    };  } - -/** - * @ngdoc service - * @name angular.module.ng.$q - * @requires $rootScope - * - * @description - * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). - * - * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an - * interface for interacting with an object that represents the result of an action that is - * performed asynchronously, and may or may not be finished at any given point in time. - * - * When it comes to error handling, deferred and promise apis are to asynchronous programing what - * `try`, `catch` and `throw` keywords are to synchronous programing. - * - * <pre> - *   function asyncGreet(name) { - *     var deferred = $q.defer(); - * - *     setTimeout(function() { - *       if (okToGreet(name)) { - *         deferred.resolve('Hello, ' + name + '!'); - *       } else { - *         deferred.reject('Greeting ' + name + ' is not allowed.'); - *       } - *     }, 1000); - * - *     return deferred.promise; - *   } - * - *   var promise = asyncGreet('Robin Hood'); - *   promise.then(function(greeting) { - *     alert('Success: ' + greeting); - *   }, function(reason) { - *     alert('Failed: ' + reason); - *   ); - * </pre> - * - * At first it might not be obvious why this extra complexity is worth the trouble. The payoff - * comes in the way of - * [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md). - * - * Additionally the promise api allows for composition that is very hard to do with the - * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. - * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the - * section on serial or paralel joining of promises. - * - * - * # The Deferred API - * - * A new instance of deferred is constructed by calling `$q.defer()`. - * - * The purpose of the deferred object is to expose the associated Promise instance as well as api's - * that can be used for singnaling the successful or unsucessful completion of the task. - * - * **Methods** - * - * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection - *   constructed via `$q.reject`, the promise will be rejected instead. - * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to - *   resolving it with a rejection constructed via `$q.reject`. - * - * **Properties** - * - * - promise – `{Promise}` – promise object associated with this deferred. - * - * - * # The Promise API - * - * A new promise instance is created when a deferred instance is created and can be retrieved by - * calling `deferred.promise`. - * - * The purpose of the promise object is to allow for interested parties to get access to the result - * of the deferred task when it completes. - * - * **Methods** - * - * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved - *   or rejected calls one of the success or error callbacks asynchronously as soon as the result - *   is available. - * - *   This method *returns a new promise* which is resolved or rejected via the return value of the - *   `successCallback` or `errorCallback`. - * - * - * # Differences betweeb Kris Kowal's Q and $q - * - *  There are three main differences: - * - * - $q is integrated with the {@link angular.module.ng.$rootScope.Scope} Scope model observation - *   mechanism in angular, which means faster propagation of resolution or rejection into your - *   models and avoiding unnecessary browser redraws, which would result in flickering UI. - * - $q promises are reconginized by the templating engine in angular, which means that in templates - *   you can treat promises attached to a scope as if they were the resulting values. - * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains - *   all the important functionality needed for common async tasks. - */ -// TODO(i): move elsewhere -function $QProvider() { - -  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { -    return qFactory(function(callback) { -      $rootScope.$evalAsync(callback); -    }, $exceptionHandler); -  }]; -} | 
