diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index d94a621d94c6..3f2beade495e 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 @@ -680,6 +680,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 (!this.$$phase && !this.$$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 6cb62d7a449b..a32538ee9b0b 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 ddd830881d9b..b22ffa664a11 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.flushNext(); + + 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.flushNext(); + + 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.flushNext(); + expect(log).toEqual(['eval-ed 1!', 'eval-ed 2!']); + + expect(function() { + $browser.defer.flushNext(); + }).toThrow('Nothing to be flushed!'); + }); + }); });