mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-08 22:37:20 +08:00
perf(Scope): remove the need for the extra watch in $watchGroup
Instead of using a counter and an extra watch, just schedule the reaction function via $evalAsync. This gives us the same/similar ordering and coalecsing of updates as counter without the extra overhead. Also the code is easier to read. Since interpolation uses watchGroup, this change additionally improves performance of interpolation. In large table benchmark digest cost went down by 15-20% for interpolation. Closes #8396
This commit is contained in:
@@ -393,9 +393,9 @@ function $RootScopeProvider(){
|
||||
var oldValues = new Array(watchExpressions.length);
|
||||
var newValues = new Array(watchExpressions.length);
|
||||
var deregisterFns = [];
|
||||
var changeCount = 0;
|
||||
var self = this;
|
||||
var masterUnwatch;
|
||||
var changeReactionScheduled = false;
|
||||
var firstRun = true;
|
||||
|
||||
if (watchExpressions.length === 1) {
|
||||
// Special case size of one
|
||||
@@ -407,29 +407,31 @@ function $RootScopeProvider(){
|
||||
}
|
||||
|
||||
forEach(watchExpressions, function (expr, i) {
|
||||
var unwatch = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
|
||||
var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
|
||||
newValues[i] = value;
|
||||
oldValues[i] = oldValue;
|
||||
changeCount++;
|
||||
}, false, function watchGroupDeregNotifier() {
|
||||
arrayRemove(deregisterFns, unwatch);
|
||||
if (!deregisterFns.length) {
|
||||
masterUnwatch();
|
||||
if (!changeReactionScheduled) {
|
||||
changeReactionScheduled = true;
|
||||
self.$evalAsync(watchGroupAction);
|
||||
}
|
||||
});
|
||||
|
||||
deregisterFns.push(unwatch);
|
||||
}, this);
|
||||
|
||||
masterUnwatch = self.$watch(function watchGroupChangeWatch() {
|
||||
return changeCount;
|
||||
}, function watchGroupChangeAction(value, oldValue) {
|
||||
listener(newValues, (value === oldValue) ? newValues : oldValues, self);
|
||||
deregisterFns.push(unwatchFn);
|
||||
});
|
||||
|
||||
function watchGroupAction() {
|
||||
changeReactionScheduled = false;
|
||||
|
||||
if (firstRun) {
|
||||
firstRun = false;
|
||||
listener(newValues, newValues, self);
|
||||
} else {
|
||||
listener(newValues, oldValues, self);
|
||||
}
|
||||
}
|
||||
|
||||
return function deregisterWatchGroup() {
|
||||
while (deregisterFns.length) {
|
||||
deregisterFns[0]();
|
||||
deregisterFns.shift()();
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -2893,21 +2893,21 @@ describe('$compile', function() {
|
||||
|
||||
inject(function($rootScope) {
|
||||
compile('<div other-tpl-dir param1="::foo" param2="bar"></div>');
|
||||
expect(countWatches($rootScope)).toEqual(7); // 5 -> template watch group, 2 -> '='
|
||||
expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '='
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:;2:;3:;4:');
|
||||
expect(countWatches($rootScope)).toEqual(7);
|
||||
expect(countWatches($rootScope)).toEqual(6);
|
||||
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:foo;2:;3:foo;4:');
|
||||
expect(countWatches($rootScope)).toEqual(5);
|
||||
expect(countWatches($rootScope)).toEqual(4);
|
||||
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar');
|
||||
expect(countWatches($rootScope)).toEqual(4);
|
||||
expect(countWatches($rootScope)).toEqual(3);
|
||||
|
||||
$rootScope.bar = 'baz';
|
||||
$rootScope.$digest();
|
||||
@@ -2927,21 +2927,21 @@ describe('$compile', function() {
|
||||
|
||||
inject(function($rootScope) {
|
||||
compile('<div other-tpl-dir param1="{{::foo}}" param2="{{bar}}"></div>');
|
||||
expect(countWatches($rootScope)).toEqual(7); // 5 -> template watch group, 2 -> {{ }}
|
||||
expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> {{ }}
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:;2:;3:;4:');
|
||||
expect(countWatches($rootScope)).toEqual(5); // (- 2) -> bind-once in template
|
||||
expect(countWatches($rootScope)).toEqual(4); // (- 2) -> bind-once in template
|
||||
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:foo;2:;3:;4:');
|
||||
expect(countWatches($rootScope)).toEqual(4);
|
||||
expect(countWatches($rootScope)).toEqual(3);
|
||||
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:foo;2:bar;3:;4:');
|
||||
expect(countWatches($rootScope)).toEqual(4);
|
||||
expect(countWatches($rootScope)).toEqual(3);
|
||||
|
||||
$rootScope.bar = 'baz';
|
||||
$rootScope.$digest();
|
||||
@@ -2964,18 +2964,18 @@ describe('$compile', function() {
|
||||
compile('<div other-tpl-dir param1="::foo" param2="bar"></div>');
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:;2:;3:;4:');
|
||||
expect(countWatches($rootScope)).toEqual(7); // 5 -> template watch group, 2 -> '='
|
||||
expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '='
|
||||
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:foo;2:;3:foo;4:');
|
||||
expect(countWatches($rootScope)).toEqual(5);
|
||||
expect(countWatches($rootScope)).toEqual(4);
|
||||
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar');
|
||||
expect(countWatches($rootScope)).toEqual(4);
|
||||
expect(countWatches($rootScope)).toEqual(3);
|
||||
|
||||
$rootScope.bar = 'baz';
|
||||
$rootScope.$digest();
|
||||
@@ -2998,18 +2998,18 @@ describe('$compile', function() {
|
||||
compile('<div other-tpl-dir param1="{{::foo}}" param2="{{bar}}"></div>');
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:;2:;3:;4:');
|
||||
expect(countWatches($rootScope)).toEqual(5); // (5 - 2) -> template watch group, 2 -> {{ }}
|
||||
expect(countWatches($rootScope)).toEqual(4); // (4 - 2) -> template watch group, 2 -> {{ }}
|
||||
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:foo;2:;3:;4:');
|
||||
expect(countWatches($rootScope)).toEqual(4);
|
||||
expect(countWatches($rootScope)).toEqual(3);
|
||||
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).toBe('1:foo;2:bar;3:;4:');
|
||||
expect(countWatches($rootScope)).toEqual(4);
|
||||
expect(countWatches($rootScope)).toEqual(3);
|
||||
|
||||
$rootScope.bar = 'baz';
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -99,11 +99,11 @@ describe('ngBind*', function() {
|
||||
it('should one-time bind the expressions that start with ::', inject(function($rootScope, $compile) {
|
||||
element = $compile('<div ng-bind-template="{{::hello}} {{::name}}!"></div>')($rootScope);
|
||||
$rootScope.name = 'Misko';
|
||||
expect($rootScope.$$watchers.length).toEqual(3);
|
||||
expect($rootScope.$$watchers.length).toEqual(2);
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-binding')).toEqual(true);
|
||||
expect(element.text()).toEqual(' Misko!');
|
||||
expect($rootScope.$$watchers.length).toEqual(2);
|
||||
expect($rootScope.$$watchers.length).toEqual(1);
|
||||
$rootScope.hello = 'Hello';
|
||||
$rootScope.name = 'Lucas';
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -149,14 +149,14 @@ describe('Scope', function() {
|
||||
|
||||
it('should clean up stable watches from $watchGroup', inject(function($rootScope) {
|
||||
$rootScope.$watchGroup(['::foo', '::bar'], function() {});
|
||||
expect($rootScope.$$watchers.length).toEqual(3);
|
||||
expect($rootScope.$$watchers.length).toEqual(2);
|
||||
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toEqual(3);
|
||||
expect($rootScope.$$watchers.length).toEqual(2);
|
||||
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toEqual(2);
|
||||
expect($rootScope.$$watchers.length).toEqual(1);
|
||||
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
|
||||
Reference in New Issue
Block a user