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); }, |
