refactor(scope): non-recursive $digest method

This commit is contained in:
Misko Hevery 2011-08-12 14:32:25 -07:00 committed by Igor Minar
parent 530dc412c4
commit 34f174066f
2 changed files with 49 additions and 42 deletions

View file

@ -317,60 +317,67 @@ Scope.prototype = {
expect(scope.counter).toEqual(1);
</pre>
*
* @returns {number} number of {@link angular.scope.$watch listeners} which fired.
*
*/
$digest: function() {
var child,
watch, value, last,
watchers = this.$$watchers,
asyncQueue = this.$$asyncQueue,
length, count = 0,
dirtyCount, ttl = 100,
recheck = !this.$parent || !this.$parent.$$phase;
var watch, value, last, next,
watchers,
asyncQueue,
length,
dirty, ttl = 100,
scope;
if (this.$$phase) {
throw Error(this.$$phase + ' already in progress');
}
this.$$phase = '$digest';
do {
while(asyncQueue.length) {
try {
this.$eval(asyncQueue.shift());
} catch (e) {
this.$service('$exceptionHandler')(e);
}
}
dirtyCount = 0;
if (watchers) {
// process our watches
length = watchers.length;
while (length--) {
dirty = false;
scope = this;
do {
scope.$$phase = '$digest';
asyncQueue = scope.$$asyncQueue;
while(asyncQueue.length) {
try {
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 ((value = watch.get(this)) !== (last = watch.last) && !equals(value, last)) {
dirtyCount++;
watch.fn(this, watch.last = copy(value), last);
}
scope.$eval(asyncQueue.shift());
} catch (e) {
this.$service('$exceptionHandler')(e);
scope.$service('$exceptionHandler')(e);
}
}
}
child = this.$$childHead;
while(child) {
dirtyCount += child.$digest();
child = child.$$nextSibling;
}
count += dirtyCount;
if (watchers = scope.$$watchers) {
// process our watches
length = watchers.length;
while (length--) {
try {
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 ((value = watch.get(scope)) !== (last = watch.last) && !equals(value, last)) {
dirty = true;
watch.fn(scope, watch.last = copy(value), last);
}
} catch (e) {
scope.$service('$exceptionHandler')(e);
}
}
}
scope.$$phase = null;
// find the next scope in traversal.
if (!(next = scope.$$childHead || scope.$$nextSibling) && scope !== this) {
do {
scope = scope.$parent;
if (scope == this || (next = scope.$$nextSibling)) {
break;
}
} while (scope !== this);
}
} while (scope = next);
if(!(ttl--)) {
throw Error('100 $digest() iterations reached. Aborting!');
}
} while (recheck && dirtyCount);
this.$$phase = null;
return count;
} while (dirty);
},
/**

View file

@ -176,7 +176,7 @@ describe('Scope', function(){
root.$digest();
log = '';
root.a = 1;
expect(root.$digest()).toEqual(3);
root.$digest();
expect(root.b).toEqual(1);
expect(root.c).toEqual(1);
expect(root.d).toEqual(1);
@ -211,7 +211,7 @@ describe('Scope', function(){
root.$watch('b', function(){ log += 'b'; });
root.$digest();
log = '';
expect(root.$digest()).toEqual(0);
root.$digest();
expect(log).toEqual('');
});