aboutsummaryrefslogtreecommitdiffstats
path: root/src/ng/rootScope.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/ng/rootScope.js')
-rw-r--r--src/ng/rootScope.js53
1 files changed, 36 insertions, 17 deletions
diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js
index a54fdc98..233dfa3b 100644
--- a/src/ng/rootScope.js
+++ b/src/ng/rootScope.js
@@ -71,6 +71,7 @@
function $RootScopeProvider(){
var TTL = 10;
var $rootScopeMinErr = minErr('$rootScope');
+ var lastDirtyWatch = null;
this.digestTtl = function(value) {
if (arguments.length) {
@@ -325,6 +326,8 @@ function $RootScopeProvider(){
eq: !!objectEquality
};
+ lastDirtyWatch = null;
+
// in the case user pass string, we need to compile it, do we really need this ?
if (!isFunction(listener)) {
var listenFn = compileToFn(listener || noop, 'listener');
@@ -553,6 +556,8 @@ function $RootScopeProvider(){
beginPhase('$digest');
+ lastDirtyWatch = null;
+
do { // "while dirty" loop
dirty = false;
current = target;
@@ -565,8 +570,10 @@ function $RootScopeProvider(){
clearPhase();
$exceptionHandler(e);
}
+ lastDirtyWatch = null;
}
+ traverseScopesLoop:
do { // "traverse the scopes" loop
if ((watchers = current.$$watchers)) {
// process our watches
@@ -576,22 +583,30 @@ function $RootScopeProvider(){
watch = watchers[length];
// Most common watches are on primitives, in which case we can short
// circuit it with === operator, only when === fails do we use .equals
- if (watch && (value = watch.get(current)) !== (last = watch.last) &&
- !(watch.eq
- ? equals(value, last)
- : (typeof value == 'number' && typeof last == 'number'
- && isNaN(value) && isNaN(last)))) {
- dirty = true;
- watch.last = watch.eq ? copy(value) : value;
- watch.fn(value, ((last === initWatchVal) ? value : last), current);
- if (ttl < 5) {
- logIdx = 4 - ttl;
- if (!watchLog[logIdx]) watchLog[logIdx] = [];
- logMsg = (isFunction(watch.exp))
- ? 'fn: ' + (watch.exp.name || watch.exp.toString())
- : watch.exp;
- logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
- watchLog[logIdx].push(logMsg);
+ if (watch) {
+ if ((value = watch.get(current)) !== (last = watch.last) &&
+ !(watch.eq
+ ? equals(value, last)
+ : (typeof value == 'number' && typeof last == 'number'
+ && isNaN(value) && isNaN(last)))) {
+ dirty = true;
+ lastDirtyWatch = watch;
+ watch.last = watch.eq ? copy(value) : value;
+ watch.fn(value, ((last === initWatchVal) ? value : last), current);
+ if (ttl < 5) {
+ logIdx = 4 - ttl;
+ if (!watchLog[logIdx]) watchLog[logIdx] = [];
+ logMsg = (isFunction(watch.exp))
+ ? 'fn: ' + (watch.exp.name || watch.exp.toString())
+ : watch.exp;
+ logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
+ watchLog[logIdx].push(logMsg);
+ }
+ } else if (watch === lastDirtyWatch) {
+ // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
+ // have already been tested.
+ dirty = false;
+ break traverseScopesLoop;
}
}
} catch (e) {
@@ -604,13 +619,16 @@ function $RootScopeProvider(){
// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
// this piece should be kept in sync with the traversal in $broadcast
- if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
+ if (!(next = (current.$$childHead ||
+ (current !== target && current.$$nextSibling)))) {
while(current !== target && !(next = current.$$nextSibling)) {
current = current.$parent;
}
}
} while ((current = next));
+ // `break traverseScopesLoop;` takes us to here
+
if(dirty && !(ttl--)) {
clearPhase();
throw $rootScopeMinErr('infdig',
@@ -618,6 +636,7 @@ function $RootScopeProvider(){
'Watchers fired in the last 5 iterations: {1}',
TTL, toJson(watchLog));
}
+
} while (dirty || asyncQueue.length);
clearPhase();