Skip to content

Commit 989446e

Browse files
committed
fix($rootScope): TTL exception does not clear $$phase
When $digest() throws infinite digest exception it does not properly clear the $phase leaving the scope in an inconsistent state. Closes angular#979
1 parent 5214c1d commit 989446e

File tree

2 files changed

+45
-12
lines changed

2 files changed

+45
-12
lines changed

src/ng/rootScope.js

+22-12
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ function $RootScopeProvider(){
372372
watchLog = [],
373373
logIdx, logMsg;
374374

375-
flagPhase(target, '$digest');
375+
beginPhase('$digest');
376376

377377
do {
378378
dirty = false;
@@ -429,12 +429,13 @@ function $RootScopeProvider(){
429429
} while ((current = next));
430430

431431
if(dirty && !(ttl--)) {
432+
clearPhase();
432433
throw Error(TTL + ' $digest() iterations reached. Aborting!\n' +
433434
'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
434435
}
435436
} while (dirty || asyncQueue.length);
436437

437-
this.$root.$$phase = null;
438+
clearPhase();
438439
},
439440

440441

@@ -469,7 +470,7 @@ function $RootScopeProvider(){
469470
* perform any necessary cleanup.
470471
*/
471472
$destroy: function() {
472-
if (this.$root == this) return; // we can't remove the root node;
473+
if ($rootScope == this) return; // we can't remove the root node;
473474
var parent = this.$parent;
474475

475476
this.$broadcast('$destroy');
@@ -586,13 +587,18 @@ function $RootScopeProvider(){
586587
*/
587588
$apply: function(expr) {
588589
try {
589-
flagPhase(this, '$apply');
590+
beginPhase('$apply');
590591
return this.$eval(expr);
591592
} catch (e) {
592593
$exceptionHandler(e);
593594
} finally {
594-
this.$root.$$phase = null;
595-
this.$root.$digest();
595+
clearPhase();
596+
try {
597+
$rootScope.$digest();
598+
} catch (e) {
599+
$exceptionHandler(e);
600+
throw e;
601+
}
596602
}
597603
},
598604

@@ -754,18 +760,22 @@ function $RootScopeProvider(){
754760
}
755761
};
756762

763+
var $rootScope = new Scope();
764+
765+
return $rootScope;
757766

758-
function flagPhase(scope, phase) {
759-
var root = scope.$root;
760767

761-
if (root.$$phase) {
762-
throw Error(root.$$phase + ' already in progress');
768+
function beginPhase(phase) {
769+
if ($rootScope.$$phase) {
770+
throw Error($rootScope.$$phase + ' already in progress');
763771
}
764772

765-
root.$$phase = phase;
773+
$rootScope.$$phase = phase;
766774
}
767775

768-
return new Scope();
776+
function clearPhase() {
777+
$rootScope.$$phase = null;
778+
}
769779

770780
function compileToFn(exp, name) {
771781
var fn = $parse(exp);

test/ng/rootScopeSpec.js

+23
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ describe('Scope', function() {
210210
'["a; newVal: 98; oldVal: 97","b; newVal: 99; oldVal: 98"],' +
211211
'["a; newVal: 99; oldVal: 98","b; newVal: 100; oldVal: 99"],' +
212212
'["a; newVal: 100; oldVal: 99","b; newVal: 101; oldVal: 100"]]');
213+
214+
expect($rootScope.$$phase).toBeNull();
213215
});
214216
});
215217

@@ -492,6 +494,27 @@ describe('Scope', function() {
492494
});
493495

494496

497+
it('should log exceptions from $digest', function() {
498+
module(function($rootScopeProvider, $exceptionHandlerProvider) {
499+
$rootScopeProvider.digestTtl(2);
500+
$exceptionHandlerProvider.mode('log');
501+
});
502+
inject(function($rootScope, $exceptionHandler) {
503+
$rootScope.$watch('a', function() {$rootScope.b++;});
504+
$rootScope.$watch('b', function() {$rootScope.a++;});
505+
$rootScope.a = $rootScope.b = 0;
506+
507+
expect(function() {
508+
$rootScope.$apply();
509+
}).toThrow();
510+
511+
expect($exceptionHandler.errors[0]).toBeDefined();
512+
513+
expect($rootScope.$$phase).toBeNull();
514+
});
515+
});
516+
517+
495518
describe('exceptions', function() {
496519
var log;
497520
beforeEach(module(function($exceptionHandlerProvider) {

0 commit comments

Comments
 (0)