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

Commit ba43692

Browse files
committed
perf($rootScope): moving internal queues out of the Scope instances
1 parent 19871d2 commit ba43692

File tree

3 files changed

+47
-29
lines changed

3 files changed

+47
-29
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['this'] = 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
/**
@@ -193,9 +190,6 @@ function $RootScopeProvider(){
193190
if (isolate) {
194191
child = new Scope();
195192
child.$root = this.$root;
196-
// ensure that there is just one async queue per $rootScope and its children
197-
child.$$asyncQueue = this.$$asyncQueue;
198-
child.$$postDigestQueue = this.$$postDigestQueue;
199193
} else {
200194
// Only create a child scope class if somebody asks for one,
201195
// but cache it to allow the VM to optimize lookups.
@@ -695,8 +689,6 @@ function $RootScopeProvider(){
695689
$digest: function() {
696690
var watch, value, last,
697691
watchers,
698-
asyncQueue = this.$$asyncQueue,
699-
postDigestQueue = this.$$postDigestQueue,
700692
length,
701693
dirty, ttl = TTL,
702694
next, current, target = this,
@@ -861,6 +853,10 @@ function $RootScopeProvider(){
861853
if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
862854
if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
863855

856+
// Disable listeners, watchers and apply/digest methods
857+
this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
858+
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
859+
this.$$listeners = {};
864860

865861
// All of the code below is bogus code that works around V8's memory leak via optimized code
866862
// and inline caches.
@@ -871,15 +867,7 @@ function $RootScopeProvider(){
871867
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
872868

873869
this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
874-
this.$$childTail = this.$root = null;
875-
876-
// don't reset these to null in case some async task tries to register a listener/watch/task
877-
this.$$listeners = {};
878-
this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = [];
879-
880-
// prevent NPEs since these methods have references to properties we nulled out
881-
this.$destroy = this.$digest = this.$apply = noop;
882-
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
870+
this.$$childTail = this.$root = this.$$watchers = null;
883871
},
884872

885873
/**
@@ -946,19 +934,19 @@ function $RootScopeProvider(){
946934
$evalAsync: function(expr) {
947935
// if we are outside of an $digest loop and this is the first time we are scheduling async
948936
// task also schedule async auto-flush
949-
if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
937+
if (!$rootScope.$$phase && !asyncQueue.length) {
950938
$browser.defer(function() {
951-
if ($rootScope.$$asyncQueue.length) {
939+
if (asyncQueue.length) {
952940
$rootScope.$digest();
953941
}
954942
});
955943
}
956944

957-
this.$$asyncQueue.push({scope: this, expression: expr});
945+
asyncQueue.push({scope: this, expression: expr});
958946
},
959947

960948
$$postDigest : function(fn) {
961-
this.$$postDigestQueue.push(fn);
949+
postDigestQueue.push(fn);
962950
},
963951

964952
/**
@@ -1042,7 +1030,7 @@ function $RootScopeProvider(){
10421030
*/
10431031
$applyAsync: function(expr) {
10441032
var scope = this;
1045-
expr && $rootScope.$$applyAsyncQueue.push($applyAsyncExpression);
1033+
expr && applyAsyncQueue.push($applyAsyncExpression);
10461034
scheduleApplyAsync();
10471035

10481036
function $applyAsyncExpression() {
@@ -1251,6 +1239,11 @@ function $RootScopeProvider(){
12511239

12521240
var $rootScope = new Scope();
12531241

1242+
//The internal queues. Expose them on the $rootScope for debugging/testing purposes.
1243+
var asyncQueue = $rootScope.$$asyncQueue = [];
1244+
var postDigestQueue = $rootScope.$$postDigestQueue = [];
1245+
var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
1246+
12541247
return $rootScope;
12551248

12561249

@@ -1284,10 +1277,9 @@ function $RootScopeProvider(){
12841277
function initWatchVal() {}
12851278

12861279
function flushApplyAsync() {
1287-
var queue = $rootScope.$$applyAsyncQueue;
1288-
while (queue.length) {
1280+
while (applyAsyncQueue.length) {
12891281
try {
1290-
queue.shift()();
1282+
applyAsyncQueue.shift()();
12911283
} catch(e) {
12921284
$exceptionHandler(e);
12931285
}

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-2
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,33 @@ describe('Scope', function() {
10381038
expect(fn).toBe(noop);
10391039
}));
10401040

1041+
it("should do nothing when $apply()ing after parent's destruction", inject(function($rootScope) {
1042+
var parent = $rootScope.$new(),
1043+
child = parent.$new();
1044+
1045+
parent.$destroy();
1046+
1047+
var called = false;
1048+
function applyFunc() { called = true; }
1049+
child.$apply(applyFunc);
1050+
1051+
expect(called).toBe(false);
1052+
}));
1053+
1054+
it("should do nothing when $evalAsync()ing after parent's destruction", inject(function($rootScope, $timeout) {
1055+
var parent = $rootScope.$new(),
1056+
child = parent.$new();
1057+
1058+
parent.$destroy();
1059+
1060+
var called = false;
1061+
function applyFunc() { called = true; }
1062+
child.$evalAsync(applyFunc);
1063+
1064+
$timeout.verifyNoPendingTasks();
1065+
expect(called).toBe(false);
1066+
}));
1067+
10411068

10421069
it("should preserve all (own and inherited) model properties on a destroyed scope",
10431070
inject(function($rootScope) {
@@ -1204,15 +1231,14 @@ describe('Scope', function() {
12041231
isolateScope.$evalAsync('isolateExpression');
12051232

12061233
expect(childScope.$$asyncQueue).toBe($rootScope.$$asyncQueue);
1207-
expect(isolateScope.$$asyncQueue).toBe($rootScope.$$asyncQueue);
1234+
expect(isolateScope.$$asyncQueue).toBeUndefined();
12081235
expect($rootScope.$$asyncQueue).toEqual([
12091236
{scope: $rootScope, expression: 'rootExpression'},
12101237
{scope: childScope, expression: 'childExpression'},
12111238
{scope: isolateScope, expression: 'isolateExpression'}
12121239
]);
12131240
}));
12141241

1215-
12161242
describe('auto-flushing when queueing outside of an $apply', function() {
12171243
var log, $rootScope, $browser;
12181244

0 commit comments

Comments
 (0)