mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
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 #979
This commit is contained in:
parent
5214c1d0cb
commit
989446ecee
2 changed files with 45 additions and 12 deletions
|
|
@ -372,7 +372,7 @@ function $RootScopeProvider(){
|
|||
watchLog = [],
|
||||
logIdx, logMsg;
|
||||
|
||||
flagPhase(target, '$digest');
|
||||
beginPhase('$digest');
|
||||
|
||||
do {
|
||||
dirty = false;
|
||||
|
|
@ -429,12 +429,13 @@ function $RootScopeProvider(){
|
|||
} while ((current = next));
|
||||
|
||||
if(dirty && !(ttl--)) {
|
||||
clearPhase();
|
||||
throw Error(TTL + ' $digest() iterations reached. Aborting!\n' +
|
||||
'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
|
||||
}
|
||||
} while (dirty || asyncQueue.length);
|
||||
|
||||
this.$root.$$phase = null;
|
||||
clearPhase();
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -469,7 +470,7 @@ function $RootScopeProvider(){
|
|||
* perform any necessary cleanup.
|
||||
*/
|
||||
$destroy: function() {
|
||||
if (this.$root == this) return; // we can't remove the root node;
|
||||
if ($rootScope == this) return; // we can't remove the root node;
|
||||
var parent = this.$parent;
|
||||
|
||||
this.$broadcast('$destroy');
|
||||
|
|
@ -586,13 +587,18 @@ function $RootScopeProvider(){
|
|||
*/
|
||||
$apply: function(expr) {
|
||||
try {
|
||||
flagPhase(this, '$apply');
|
||||
beginPhase('$apply');
|
||||
return this.$eval(expr);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
} finally {
|
||||
this.$root.$$phase = null;
|
||||
this.$root.$digest();
|
||||
clearPhase();
|
||||
try {
|
||||
$rootScope.$digest();
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -754,18 +760,22 @@ function $RootScopeProvider(){
|
|||
}
|
||||
};
|
||||
|
||||
var $rootScope = new Scope();
|
||||
|
||||
function flagPhase(scope, phase) {
|
||||
var root = scope.$root;
|
||||
return $rootScope;
|
||||
|
||||
if (root.$$phase) {
|
||||
throw Error(root.$$phase + ' already in progress');
|
||||
|
||||
function beginPhase(phase) {
|
||||
if ($rootScope.$$phase) {
|
||||
throw Error($rootScope.$$phase + ' already in progress');
|
||||
}
|
||||
|
||||
root.$$phase = phase;
|
||||
$rootScope.$$phase = phase;
|
||||
}
|
||||
|
||||
return new Scope();
|
||||
function clearPhase() {
|
||||
$rootScope.$$phase = null;
|
||||
}
|
||||
|
||||
function compileToFn(exp, name) {
|
||||
var fn = $parse(exp);
|
||||
|
|
|
|||
|
|
@ -210,6 +210,8 @@ describe('Scope', function() {
|
|||
'["a; newVal: 98; oldVal: 97","b; newVal: 99; oldVal: 98"],' +
|
||||
'["a; newVal: 99; oldVal: 98","b; newVal: 100; oldVal: 99"],' +
|
||||
'["a; newVal: 100; oldVal: 99","b; newVal: 101; oldVal: 100"]]');
|
||||
|
||||
expect($rootScope.$$phase).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -492,6 +494,27 @@ describe('Scope', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should log exceptions from $digest', function() {
|
||||
module(function($rootScopeProvider, $exceptionHandlerProvider) {
|
||||
$rootScopeProvider.digestTtl(2);
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
});
|
||||
inject(function($rootScope, $exceptionHandler) {
|
||||
$rootScope.$watch('a', function() {$rootScope.b++;});
|
||||
$rootScope.$watch('b', function() {$rootScope.a++;});
|
||||
$rootScope.a = $rootScope.b = 0;
|
||||
|
||||
expect(function() {
|
||||
$rootScope.$apply();
|
||||
}).toThrow();
|
||||
|
||||
expect($exceptionHandler.errors[0]).toBeDefined();
|
||||
|
||||
expect($rootScope.$$phase).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('exceptions', function() {
|
||||
var log;
|
||||
beforeEach(module(function($exceptionHandlerProvider) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue