aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Minar2011-10-30 15:11:25 -0700
committerIgor Minar2011-10-31 11:34:25 -0700
commitef875ad0cf4349144cb4674e050dd160564f6dd9 (patch)
tree77b5aaac971cb6a5b2e43504db8886b9877da89b
parent615841a5d3cb6dae8329411c27fd938e9b413f4c (diff)
downloadangular.js-ef875ad0cf4349144cb4674e050dd160564f6dd9.tar.bz2
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
-rw-r--r--src/Scope.js17
-rw-r--r--test/ScopeSpec.js28
2 files changed, 38 insertions, 7 deletions
diff --git a/src/Scope.js b/src/Scope.js
index be5030cc..c4b9513b 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -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);
},
diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js
index b1942646..e1f8181e 100644
--- a/test/ScopeSpec.js
+++ b/test/ScopeSpec.js
@@ -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;}"]]');
});