diff options
| author | Noam Lewis | 2013-12-30 20:10:23 -0500 | 
|---|---|---|
| committer | Jeff Cross | 2014-01-13 09:53:38 -0800 | 
| commit | 2cd09c9f0e7766bcd191662841b7b1ffc3b6dc3f (patch) | |
| tree | a4cd4eebf7376ebd9370fe96f2cabb28f74e74eb | |
| parent | 34fee06ca7db16549764f53c0382f2b6e2482201 (diff) | |
| download | angular.js-2cd09c9f0e7766bcd191662841b7b1ffc3b6dc3f.tar.bz2 | |
fix($rootScope): prevent infinite $digest by checking if asyncQueue is empty when decrementing ttl
An infinite $digest loop can be caused by expressions that invoke a promise.
The problem is that $digest does not decrement ttl unless it finds dirty changes;
it should check also if asyncQueue is empty.
Generally the condition for decrementing ttl should be the same as the
condition for terminating the $digest loop.
Fixes #2622
| -rw-r--r-- | src/ng/rootScope.js | 2 | ||||
| -rw-r--r-- | test/ng/rootScopeSpec.js | 25 | 
2 files changed, 26 insertions, 1 deletions
| diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index 8259bf88..bbf290b2 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -632,7 +632,7 @@ function $RootScopeProvider(){            // `break traverseScopesLoop;` takes us to here -          if(dirty && !(ttl--)) { +          if((dirty || asyncQueue.length) && !(ttl--)) {              clearPhase();              throw $rootScopeMinErr('infdig',                  '{0} $digest() iterations reached. Aborting!\n' + diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index e47111e2..f9cf9412 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -258,6 +258,31 @@ describe('Scope', function() {      })); +    it('should prevent infinite loop when creating and resolving a promise in a watched expression', function() { +      module(function($rootScopeProvider) { +          $rootScopeProvider.digestTtl(10); +      }); +      inject(function($rootScope, $q) { +          var d = $q.defer(); + +          d.resolve('Hello, world.'); +          $rootScope.$watch(function () { +              var $d2 = $q.defer(); +              $d2.resolve('Goodbye.'); +              $d2.promise.then(function () { }); +              return d.promise; +          }, function () { return 0; }); + +          expect(function() { +              $rootScope.$digest(); +          }).toThrowMinErr('$rootScope', 'infdig', '10 $digest() iterations reached. Aborting!\n'+ +                  'Watchers fired in the last 5 iterations: []'); + +          expect($rootScope.$$phase).toBeNull(); +      }); +      }); + +      it('should not fire upon $watch registration on initial $digest', inject(function($rootScope) {        var log = '';        $rootScope.a = 1; | 
