mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-04-18 22:01:01 +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 = [],
|
watchLog = [],
|
||||||
logIdx, logMsg;
|
logIdx, logMsg;
|
||||||
|
|
||||||
flagPhase(target, '$digest');
|
beginPhase('$digest');
|
||||||
|
|
||||||
do {
|
do {
|
||||||
dirty = false;
|
dirty = false;
|
||||||
|
|
@ -429,12 +429,13 @@ function $RootScopeProvider(){
|
||||||
} while ((current = next));
|
} while ((current = next));
|
||||||
|
|
||||||
if(dirty && !(ttl--)) {
|
if(dirty && !(ttl--)) {
|
||||||
|
clearPhase();
|
||||||
throw Error(TTL + ' $digest() iterations reached. Aborting!\n' +
|
throw Error(TTL + ' $digest() iterations reached. Aborting!\n' +
|
||||||
'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
|
'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
|
||||||
}
|
}
|
||||||
} while (dirty || asyncQueue.length);
|
} while (dirty || asyncQueue.length);
|
||||||
|
|
||||||
this.$root.$$phase = null;
|
clearPhase();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -469,7 +470,7 @@ function $RootScopeProvider(){
|
||||||
* perform any necessary cleanup.
|
* perform any necessary cleanup.
|
||||||
*/
|
*/
|
||||||
$destroy: function() {
|
$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;
|
var parent = this.$parent;
|
||||||
|
|
||||||
this.$broadcast('$destroy');
|
this.$broadcast('$destroy');
|
||||||
|
|
@ -586,13 +587,18 @@ function $RootScopeProvider(){
|
||||||
*/
|
*/
|
||||||
$apply: function(expr) {
|
$apply: function(expr) {
|
||||||
try {
|
try {
|
||||||
flagPhase(this, '$apply');
|
beginPhase('$apply');
|
||||||
return this.$eval(expr);
|
return this.$eval(expr);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$exceptionHandler(e);
|
$exceptionHandler(e);
|
||||||
} finally {
|
} finally {
|
||||||
this.$root.$$phase = null;
|
clearPhase();
|
||||||
this.$root.$digest();
|
try {
|
||||||
|
$rootScope.$digest();
|
||||||
|
} catch (e) {
|
||||||
|
$exceptionHandler(e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -754,18 +760,22 @@ function $RootScopeProvider(){
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var $rootScope = new Scope();
|
||||||
|
|
||||||
function flagPhase(scope, phase) {
|
return $rootScope;
|
||||||
var root = scope.$root;
|
|
||||||
|
|
||||||
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) {
|
function compileToFn(exp, name) {
|
||||||
var fn = $parse(exp);
|
var fn = $parse(exp);
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,8 @@ describe('Scope', function() {
|
||||||
'["a; newVal: 98; oldVal: 97","b; newVal: 99; oldVal: 98"],' +
|
'["a; newVal: 98; oldVal: 97","b; newVal: 99; oldVal: 98"],' +
|
||||||
'["a; newVal: 99; oldVal: 98","b; newVal: 100; oldVal: 99"],' +
|
'["a; newVal: 99; oldVal: 98","b; newVal: 100; oldVal: 99"],' +
|
||||||
'["a; newVal: 100; oldVal: 99","b; newVal: 101; oldVal: 100"]]');
|
'["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() {
|
describe('exceptions', function() {
|
||||||
var log;
|
var log;
|
||||||
beforeEach(module(function($exceptionHandlerProvider) {
|
beforeEach(module(function($exceptionHandlerProvider) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue