feat(scope): better logging of infinite digest error

Feedback team has often problems debugging inifinite digest errors, this change
should reveal info about what watchers are causing the infinite loop
This commit is contained in:
Igor Minar
2011-10-30 15:11:25 -07:00
parent 615841a5d3
commit ef875ad0cf
2 changed files with 38 additions and 7 deletions

View File

@@ -260,7 +260,8 @@ Scope.prototype = {
watcher = {
fn: listenFn,
last: Number.NaN, // NaN !== NaN. We used this to force $watch to fire on first run.
get: get
get: get,
exp: watchExp
};
if (!array) {
@@ -325,7 +326,8 @@ Scope.prototype = {
asyncQueue,
length,
dirty, ttl = 100,
next, current, target = this;
next, current, target = this,
watchLog = [];
if (target.$$phase) {
throw Error(target.$$phase + ' already in progress');
@@ -356,6 +358,14 @@ Scope.prototype = {
dirty = true;
watch.last = copy(value);
watch.fn(current, value, last);
if (ttl < 5) {
if (!watchLog[4-ttl]) watchLog[4-ttl] = [];
if (isFunction(watch.exp)) {
watchLog[4-ttl].push('fn: ' + (watch.exp.name || watch.exp.toString()));
} else {
watchLog[4-ttl].push(watch.exp);
}
}
}
} catch (e) {
current.$service('$exceptionHandler')(e);
@@ -376,7 +386,8 @@ Scope.prototype = {
} while ((current = next));
if(!(ttl--)) {
throw Error('100 $digest() iterations reached. Aborting!');
throw Error('100 $digest() iterations reached. Aborting!\n' +
'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
}
} while (dirty);
},

View File

@@ -218,14 +218,34 @@ describe('Scope', function() {
});
it('should prevent infinite recursion', function() {
root.$watch('a', function(self, v){self.b++;});
root.$watch('b', function(self, v){self.a++;});
it('should prevent infinite recursion and print watcher expression', function() {
root.$watch('a', function(self){self.b++;});
root.$watch('b', function(self){self.a++;});
root.a = root.b = 0;
expect(function() {
root.$digest();
}).toThrow('100 $digest() iterations reached. Aborting!');
}).toThrow('100 $digest() iterations reached. Aborting!\n'+
'Watchers fired in the last 5 iterations: ' +
'[["a","b"],["a","b"],["a","b"],["a","b"],["a","b"]]');
});
it('should prevent infinite recurcion and print print watcher function name or body',
function() {
root.$watch(function watcherA() {return root.a;}, function(self){self.b++;});
root.$watch(function() {return root.b;}, function(self){self.a++;});
root.a = root.b = 0;
expect(function() {
root.$digest();
}).toThrow('100 $digest() iterations reached. Aborting!\n'+
'Watchers fired in the last 5 iterations: ' +
'[["fn: watcherA","fn: function () {return root.b;}"],'+
'["fn: watcherA","fn: function () {return root.b;}"],'+
'["fn: watcherA","fn: function () {return root.b;}"],'+
'["fn: watcherA","fn: function () {return root.b;}"],'+
'["fn: watcherA","fn: function () {return root.b;}"]]');
});