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

Commit b119251

Browse files
jbedardIgorMinar
authored andcommitted
perf($rootScope): moving internal queues out of the Scope instances
Closes #9071
1 parent 5572b40 commit b119251

File tree

3 files changed

+47
-28
lines changed

3 files changed

+47
-28
lines changed

src/ng/rootScope.js

+17-25
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,9 @@ function $RootScopeProvider(){
130130
this.$$childHead = this.$$childTail = null;
131131
this.$root = this;
132132
this.$$destroyed = false;
133-
this.$$asyncQueue = [];
134-
this.$$postDigestQueue = [];
135133
this.$$listeners = {};
136134
this.$$listenerCount = {};
137135
this.$$isolateBindings = null;
138-
this.$$applyAsyncQueue = [];
139136
}
140137

141138
/**
@@ -201,9 +198,6 @@ function $RootScopeProvider(){
201198
if (isolate) {
202199
child = new Scope();
203200
child.$root = this.$root;
204-
// ensure that there is just one async queue per $rootScope and its children
205-
child.$$asyncQueue = this.$$asyncQueue;
206-
child.$$postDigestQueue = this.$$postDigestQueue;
207201
} else {
208202
// Only create a child scope class if somebody asks for one,
209203
// but cache it to allow the VM to optimize lookups.
@@ -704,8 +698,6 @@ function $RootScopeProvider(){
704698
$digest: function() {
705699
var watch, value, last,
706700
watchers,
707-
asyncQueue = this.$$asyncQueue,
708-
postDigestQueue = this.$$postDigestQueue,
709701
length,
710702
dirty, ttl = TTL,
711703
next, current, target = this,
@@ -870,6 +862,10 @@ function $RootScopeProvider(){
870862
if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
871863
if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
872864

865+
// Disable listeners, watchers and apply/digest methods
866+
this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
867+
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
868+
this.$$listeners = {};
873869

874870
// All of the code below is bogus code that works around V8's memory leak via optimized code
875871
// and inline caches.
@@ -880,15 +876,7 @@ function $RootScopeProvider(){
880876
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
881877

882878
this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
883-
this.$$childTail = this.$root = null;
884-
885-
// don't reset these to null in case some async task tries to register a listener/watch/task
886-
this.$$listeners = {};
887-
this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = [];
888-
889-
// prevent NPEs since these methods have references to properties we nulled out
890-
this.$destroy = this.$digest = this.$apply = noop;
891-
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
879+
this.$$childTail = this.$root = this.$$watchers = null;
892880
},
893881

894882
/**
@@ -955,19 +943,19 @@ function $RootScopeProvider(){
955943
$evalAsync: function(expr) {
956944
// if we are outside of an $digest loop and this is the first time we are scheduling async
957945
// task also schedule async auto-flush
958-
if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
946+
if (!$rootScope.$$phase && !asyncQueue.length) {
959947
$browser.defer(function() {
960-
if ($rootScope.$$asyncQueue.length) {
948+
if (asyncQueue.length) {
961949
$rootScope.$digest();
962950
}
963951
});
964952
}
965953

966-
this.$$asyncQueue.push({scope: this, expression: expr});
954+
asyncQueue.push({scope: this, expression: expr});
967955
},
968956

969957
$$postDigest : function(fn) {
970-
this.$$postDigestQueue.push(fn);
958+
postDigestQueue.push(fn);
971959
},
972960

973961
/**
@@ -1051,7 +1039,7 @@ function $RootScopeProvider(){
10511039
*/
10521040
$applyAsync: function(expr) {
10531041
var scope = this;
1054-
expr && $rootScope.$$applyAsyncQueue.push($applyAsyncExpression);
1042+
expr && applyAsyncQueue.push($applyAsyncExpression);
10551043
scheduleApplyAsync();
10561044

10571045
function $applyAsyncExpression() {
@@ -1260,6 +1248,11 @@ function $RootScopeProvider(){
12601248

12611249
var $rootScope = new Scope();
12621250

1251+
//The internal queues. Expose them on the $rootScope for debugging/testing purposes.
1252+
var asyncQueue = $rootScope.$$asyncQueue = [];
1253+
var postDigestQueue = $rootScope.$$postDigestQueue = [];
1254+
var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
1255+
12631256
return $rootScope;
12641257

12651258

@@ -1293,10 +1286,9 @@ function $RootScopeProvider(){
12931286
function initWatchVal() {}
12941287

12951288
function flushApplyAsync() {
1296-
var queue = $rootScope.$$applyAsyncQueue;
1297-
while (queue.length) {
1289+
while (applyAsyncQueue.length) {
12981290
try {
1299-
queue.shift()();
1291+
applyAsyncQueue.shift()();
13001292
} catch(e) {
13011293
$exceptionHandler(e);
13021294
}

test/ng/directive/ngBindSpec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,13 @@ describe('ngBind*', function() {
160160
it('should NOT set html for untrusted values', inject(function($rootScope, $compile) {
161161
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
162162
$rootScope.html = '<div onclick="">hello</div>';
163-
expect($rootScope.$digest).toThrow();
163+
expect(function() { $rootScope.$digest(); }).toThrow();
164164
}));
165165

166166
it('should NOT set html for wrongly typed values', inject(function($rootScope, $compile, $sce) {
167167
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
168168
$rootScope.html = $sce.trustAsCss('<div onclick="">hello</div>');
169-
expect($rootScope.$digest).toThrow();
169+
expect(function() { $rootScope.$digest(); }).toThrow();
170170
}));
171171

172172
it('should set html for trusted values', inject(function($rootScope, $compile, $sce) {

test/ng/rootScopeSpec.js

+28-1
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,33 @@ describe('Scope', function() {
10641064
expect(fn).toBe(noop);
10651065
}));
10661066

1067+
it("should do nothing when $apply()ing after parent's destruction", inject(function($rootScope) {
1068+
var parent = $rootScope.$new(),
1069+
child = parent.$new();
1070+
1071+
parent.$destroy();
1072+
1073+
var called = false;
1074+
function applyFunc() { called = true; }
1075+
child.$apply(applyFunc);
1076+
1077+
expect(called).toBe(false);
1078+
}));
1079+
1080+
it("should do nothing when $evalAsync()ing after parent's destruction", inject(function($rootScope, $timeout) {
1081+
var parent = $rootScope.$new(),
1082+
child = parent.$new();
1083+
1084+
parent.$destroy();
1085+
1086+
var called = false;
1087+
function applyFunc() { called = true; }
1088+
child.$evalAsync(applyFunc);
1089+
1090+
$timeout.verifyNoPendingTasks();
1091+
expect(called).toBe(false);
1092+
}));
1093+
10671094

10681095
it("should preserve all (own and inherited) model properties on a destroyed scope",
10691096
inject(function($rootScope) {
@@ -1230,7 +1257,7 @@ describe('Scope', function() {
12301257
isolateScope.$evalAsync('isolateExpression');
12311258

12321259
expect(childScope.$$asyncQueue).toBe($rootScope.$$asyncQueue);
1233-
expect(isolateScope.$$asyncQueue).toBe($rootScope.$$asyncQueue);
1260+
expect(isolateScope.$$asyncQueue).toBeUndefined();
12341261
expect($rootScope.$$asyncQueue).toEqual([
12351262
{scope: $rootScope, expression: 'rootExpression'},
12361263
{scope: childScope, expression: 'childExpression'},

0 commit comments

Comments
 (0)