aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ng/rootScope.js23
-rw-r--r--src/ng/timeout.js2
-rw-r--r--test/ng/rootScopeSpec.js51
3 files changed, 70 insertions, 6 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);
},
diff --git a/src/ng/timeout.js b/src/ng/timeout.js
index 6cb62d7a..a32538ee 100644
--- a/src/ng/timeout.js
+++ b/src/ng/timeout.js
@@ -36,7 +36,7 @@ function $TimeoutProvider() {
var deferred = $q.defer(),
promise = deferred.promise,
skipApply = (isDefined(invokeApply) && !invokeApply),
- timeoutId, cleanup;
+ timeoutId;
timeoutId = $browser.defer(function() {
try {
diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js
index ddd83088..0a85d9a8 100644
--- a/test/ng/rootScopeSpec.js
+++ b/test/ng/rootScopeSpec.js
@@ -705,6 +705,57 @@ describe('Scope', function() {
expect(isolateScope.$$asyncQueue).toBe($rootScope.$$asyncQueue);
expect($rootScope.$$asyncQueue).toEqual(['rootExpression', 'childExpression', 'isolateExpression']);
}));
+
+
+ describe('auto-flushing when queueing outside of an $apply', function() {
+ var log, $rootScope, $browser;
+
+ beforeEach(inject(function(_log_, _$rootScope_, _$browser_) {
+ log = _log_;
+ $rootScope = _$rootScope_;
+ $browser = _$browser_;
+ }));
+
+
+ it('should auto-flush the queue asynchronously and trigger digest', function() {
+ $rootScope.$evalAsync(log.fn('eval-ed!'));
+ $rootScope.$watch(log.fn('digesting'));
+ expect(log).toEqual([]);
+
+ $browser.defer.flush(0);
+
+ expect(log).toEqual(['eval-ed!', 'digesting', 'digesting']);
+ });
+
+
+ it('should not trigger digest asynchronously if the queue is empty in the next tick', function() {
+ $rootScope.$evalAsync(log.fn('eval-ed!'));
+ $rootScope.$watch(log.fn('digesting'));
+ expect(log).toEqual([]);
+
+ $rootScope.$digest();
+
+ expect(log).toEqual(['eval-ed!', 'digesting', 'digesting']);
+ log.reset();
+
+ $browser.defer.flush(0);
+
+ expect(log).toEqual([]);
+ });
+
+
+ it('should not schedule more than one auto-flush task', function() {
+ $rootScope.$evalAsync(log.fn('eval-ed 1!'));
+ $rootScope.$evalAsync(log.fn('eval-ed 2!'));
+
+ $browser.defer.flush(0);
+ expect(log).toEqual(['eval-ed 1!', 'eval-ed 2!']);
+
+ expect(function() {
+ $browser.defer.flush(0);
+ }).toThrow('No deferred tasks with delay up to 0ms to be flushed!');
+ });
+ });
});