diff options
| author | Igor Minar | 2013-08-22 02:08:41 -0700 | 
|---|---|---|
| committer | Igor Minar | 2013-08-26 09:06:25 -0700 | 
| commit | 6b91aa0a18098100e5f50ea911ee135b50680d67 (patch) | |
| tree | 636d84df13da7afa31b6ac9d3db45dc00e78c2d4 /src/ng/rootScope.js | |
| parent | 42af8eada2803a54a98b4f792e60feb480d68a0c (diff) | |
| download | angular.js-6b91aa0a18098100e5f50ea911ee135b50680d67.tar.bz2 | |
feat(Scope): async auto-flush $evalAsync queue when outside of $digest
This change causes a new $digest to be scheduled in the next tick if
a task was was sent to the $evalAsync queue from outside of a $digest
or an $apply.
While this mode of operation is not common for most of the user code,
this change means that $q promises that utilze $evalAsync queue to
guarantee asynchronicity of promise apis will now also resolve outside
of a $digest, which turned out to be a big pain point for some developers.
The implementation ensures that we don't do more work than needed and
that we coalese as much work as possible into a single $digest.
The use of $browser instead of setTimeout ensures that we can mock out
and control the scheduling of "auto-flush", which should in theory
allow all of the existing code and tests to work without negative
side-effects.
Closes #3539
Closes #2438
Diffstat (limited to 'src/ng/rootScope.js')
| -rw-r--r-- | src/ng/rootScope.js | 23 | 
1 files changed, 18 insertions, 5 deletions
| diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index d94a621d..18c54434 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -69,8 +69,8 @@ function $RootScopeProvider(){      return TTL;    }; -  this.$get = ['$injector', '$exceptionHandler', '$parse', -      function( $injector,   $exceptionHandler,   $parse) { +  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', +      function( $injector,   $exceptionHandler,   $parse,   $browser) {      /**       * @ngdoc function @@ -666,13 +666,16 @@ function $RootScopeProvider(){         *         * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that:         * -       *   - it will execute in the current script execution context (before any DOM rendering). -       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after -       *     `expression` execution. +       *   - it will execute after the function that schedule the evaluation is done running (preferably before DOM rendering). +       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after `expression` execution.         *         * Any exceptions from the execution of the expression are forwarded to the         * {@link ng.$exceptionHandler $exceptionHandler} service.         * +       * __Note:__ if this function is called outside of `$digest` cycle, a new $digest cycle will be scheduled. +       * It is however encouraged to always call code that changes the model from withing an `$apply` call. +       * That includes code evaluated via `$evalAsync`. +       *         * @param {(string|function())=} expression An angular expression to be executed.         *         *    - `string`: execute using the rules as defined in  {@link guide/expression expression}. @@ -680,6 +683,16 @@ function $RootScopeProvider(){         *         */        $evalAsync: function(expr) { +        // if we are outside of an $digest loop and this is the first time we are scheduling async task also schedule +        // async auto-flush +        if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) { +          $browser.defer(function() { +            if ($rootScope.$$asyncQueue.length) { +              $rootScope.$digest(); +            } +          }); +        } +          this.$$asyncQueue.push(expr);        }, | 
