Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 3f0e642

Browse files
IgorMinarrodyhaddad
authored andcommitted
perf(Scope): use remove the need for the extra watch in $watchGroup
Instead of using a counter and an extra watch, just schedule the reaction function via . 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
1 parent 1a05daf commit 3f0e642

File tree

4 files changed

+37
-35
lines changed

4 files changed

+37
-35
lines changed

src/ng/rootScope.js

+18-16
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,9 @@ function $RootScopeProvider(){
393393
var oldValues = new Array(watchExpressions.length);
394394
var newValues = new Array(watchExpressions.length);
395395
var deregisterFns = [];
396-
var changeCount = 0;
397396
var self = this;
398-
var masterUnwatch;
397+
var changeReactionScheduled = false;
398+
var firstRun = true;
399399

400400
if (watchExpressions.length === 1) {
401401
// Special case size of one
@@ -407,29 +407,31 @@ function $RootScopeProvider(){
407407
}
408408

409409
forEach(watchExpressions, function (expr, i) {
410-
var unwatch = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
410+
var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
411411
newValues[i] = value;
412412
oldValues[i] = oldValue;
413-
changeCount++;
414-
}, false, function watchGroupDeregNotifier() {
415-
arrayRemove(deregisterFns, unwatch);
416-
if (!deregisterFns.length) {
417-
masterUnwatch();
413+
if (!changeReactionScheduled) {
414+
changeReactionScheduled = true;
415+
self.$evalAsync(watchGroupAction);
418416
}
419417
});
418+
deregisterFns.push(unwatchFn);
419+
});
420420

421-
deregisterFns.push(unwatch);
422-
}, this);
421+
function watchGroupAction() {
422+
changeReactionScheduled = false;
423423

424-
masterUnwatch = self.$watch(function watchGroupChangeWatch() {
425-
return changeCount;
426-
}, function watchGroupChangeAction(value, oldValue) {
427-
listener(newValues, (value === oldValue) ? newValues : oldValues, self);
428-
});
424+
if (firstRun) {
425+
firstRun = false;
426+
listener(newValues, newValues, self);
427+
} else {
428+
listener(newValues, oldValues, self);
429+
}
430+
}
429431

430432
return function deregisterWatchGroup() {
431433
while (deregisterFns.length) {
432-
deregisterFns[0]();
434+
deregisterFns.shift()();
433435
}
434436
};
435437
},

test/ng/compileSpec.js

+14-14
Original file line numberDiff line numberDiff line change
@@ -2915,21 +2915,21 @@ describe('$compile', function() {
29152915

29162916
inject(function($rootScope) {
29172917
compile('<div other-tpl-dir param1="::foo" param2="bar"></div>');
2918-
expect(countWatches($rootScope)).toEqual(7); // 5 -> template watch group, 2 -> '='
2918+
expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '='
29192919
$rootScope.$digest();
29202920
expect(element.html()).toBe('1:;2:;3:;4:');
2921-
expect(countWatches($rootScope)).toEqual(7);
2921+
expect(countWatches($rootScope)).toEqual(6);
29222922

29232923
$rootScope.foo = 'foo';
29242924
$rootScope.$digest();
29252925
expect(element.html()).toBe('1:foo;2:;3:foo;4:');
2926-
expect(countWatches($rootScope)).toEqual(5);
2926+
expect(countWatches($rootScope)).toEqual(4);
29272927

29282928
$rootScope.foo = 'baz';
29292929
$rootScope.bar = 'bar';
29302930
$rootScope.$digest();
29312931
expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar');
2932-
expect(countWatches($rootScope)).toEqual(4);
2932+
expect(countWatches($rootScope)).toEqual(3);
29332933

29342934
$rootScope.bar = 'baz';
29352935
$rootScope.$digest();
@@ -2949,21 +2949,21 @@ describe('$compile', function() {
29492949

29502950
inject(function($rootScope) {
29512951
compile('<div other-tpl-dir param1="{{::foo}}" param2="{{bar}}"></div>');
2952-
expect(countWatches($rootScope)).toEqual(7); // 5 -> template watch group, 2 -> {{ }}
2952+
expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> {{ }}
29532953
$rootScope.$digest();
29542954
expect(element.html()).toBe('1:;2:;3:;4:');
2955-
expect(countWatches($rootScope)).toEqual(5); // (- 2) -> bind-once in template
2955+
expect(countWatches($rootScope)).toEqual(4); // (- 2) -> bind-once in template
29562956

29572957
$rootScope.foo = 'foo';
29582958
$rootScope.$digest();
29592959
expect(element.html()).toBe('1:foo;2:;3:;4:');
2960-
expect(countWatches($rootScope)).toEqual(4);
2960+
expect(countWatches($rootScope)).toEqual(3);
29612961

29622962
$rootScope.foo = 'baz';
29632963
$rootScope.bar = 'bar';
29642964
$rootScope.$digest();
29652965
expect(element.html()).toBe('1:foo;2:bar;3:;4:');
2966-
expect(countWatches($rootScope)).toEqual(4);
2966+
expect(countWatches($rootScope)).toEqual(3);
29672967

29682968
$rootScope.bar = 'baz';
29692969
$rootScope.$digest();
@@ -2986,18 +2986,18 @@ describe('$compile', function() {
29862986
compile('<div other-tpl-dir param1="::foo" param2="bar"></div>');
29872987
$rootScope.$digest();
29882988
expect(element.html()).toBe('1:;2:;3:;4:');
2989-
expect(countWatches($rootScope)).toEqual(7); // 5 -> template watch group, 2 -> '='
2989+
expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '='
29902990

29912991
$rootScope.foo = 'foo';
29922992
$rootScope.$digest();
29932993
expect(element.html()).toBe('1:foo;2:;3:foo;4:');
2994-
expect(countWatches($rootScope)).toEqual(5);
2994+
expect(countWatches($rootScope)).toEqual(4);
29952995

29962996
$rootScope.foo = 'baz';
29972997
$rootScope.bar = 'bar';
29982998
$rootScope.$digest();
29992999
expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar');
3000-
expect(countWatches($rootScope)).toEqual(4);
3000+
expect(countWatches($rootScope)).toEqual(3);
30013001

30023002
$rootScope.bar = 'baz';
30033003
$rootScope.$digest();
@@ -3020,18 +3020,18 @@ describe('$compile', function() {
30203020
compile('<div other-tpl-dir param1="{{::foo}}" param2="{{bar}}"></div>');
30213021
$rootScope.$digest();
30223022
expect(element.html()).toBe('1:;2:;3:;4:');
3023-
expect(countWatches($rootScope)).toEqual(5); // (5 - 2) -> template watch group, 2 -> {{ }}
3023+
expect(countWatches($rootScope)).toEqual(4); // (4 - 2) -> template watch group, 2 -> {{ }}
30243024

30253025
$rootScope.foo = 'foo';
30263026
$rootScope.$digest();
30273027
expect(element.html()).toBe('1:foo;2:;3:;4:');
3028-
expect(countWatches($rootScope)).toEqual(4);
3028+
expect(countWatches($rootScope)).toEqual(3);
30293029

30303030
$rootScope.foo = 'baz';
30313031
$rootScope.bar = 'bar';
30323032
$rootScope.$digest();
30333033
expect(element.html()).toBe('1:foo;2:bar;3:;4:');
3034-
expect(countWatches($rootScope)).toEqual(4);
3034+
expect(countWatches($rootScope)).toEqual(3);
30353035

30363036
$rootScope.bar = 'baz';
30373037
$rootScope.$digest();

test/ng/directive/ngBindSpec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ describe('ngBind*', function() {
9999
it('should one-time bind the expressions that start with ::', inject(function($rootScope, $compile) {
100100
element = $compile('<div ng-bind-template="{{::hello}} {{::name}}!"></div>')($rootScope);
101101
$rootScope.name = 'Misko';
102-
expect($rootScope.$$watchers.length).toEqual(3);
102+
expect($rootScope.$$watchers.length).toEqual(2);
103103
$rootScope.$digest();
104104
expect(element.hasClass('ng-binding')).toEqual(true);
105105
expect(element.text()).toEqual(' Misko!');
106-
expect($rootScope.$$watchers.length).toEqual(2);
106+
expect($rootScope.$$watchers.length).toEqual(1);
107107
$rootScope.hello = 'Hello';
108108
$rootScope.name = 'Lucas';
109109
$rootScope.$digest();

test/ng/rootScopeSpec.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,14 @@ describe('Scope', function() {
149149

150150
it('should clean up stable watches from $watchGroup', inject(function($rootScope) {
151151
$rootScope.$watchGroup(['::foo', '::bar'], function() {});
152-
expect($rootScope.$$watchers.length).toEqual(3);
152+
expect($rootScope.$$watchers.length).toEqual(2);
153153

154154
$rootScope.$digest();
155-
expect($rootScope.$$watchers.length).toEqual(3);
155+
expect($rootScope.$$watchers.length).toEqual(2);
156156

157157
$rootScope.foo = 'foo';
158158
$rootScope.$digest();
159-
expect($rootScope.$$watchers.length).toEqual(2);
159+
expect($rootScope.$$watchers.length).toEqual(1);
160160

161161
$rootScope.bar = 'bar';
162162
$rootScope.$digest();

0 commit comments

Comments
 (0)