From 80e7a4558490f7ffd33d142844b9153a5ed00e86 Mon Sep 17 00:00:00 2001 From: Karl Seamon Date: Tue, 10 Dec 2013 17:50:30 -0500 Subject: perf(Scope): limit propagation of $broadcast to scopes that have listeners for the event Update $on and $destroy to maintain a count of event keys registered for each scope and its children. $broadcast will not descend past a node that has a count of 0/undefined for the $broadcasted event key. Closes #5341 Closes #5371 --- test/ng/rootScopeSpec.js | 126 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 107 insertions(+), 19 deletions(-) (limited to 'test/ng/rootScopeSpec.js') diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index cc6727c2..3677ccc8 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -730,6 +730,28 @@ describe('Scope', function() { first.$apply(); expect(log).toBe('1232323'); })); + + + it('should decrement anscestor $$listenerCount entries', inject(function($rootScope) { + var EVENT = 'fooEvent', + spy = jasmine.createSpy('listener'), + firstSecond = first.$new(); + + firstSecond.$on(EVENT, spy); + firstSecond.$on(EVENT, spy); + middle.$on(EVENT, spy); + + expect($rootScope.$$listenerCount[EVENT]).toBe(3); + expect(first.$$listenerCount[EVENT]).toBe(2); + + firstSecond.$destroy(); + + expect($rootScope.$$listenerCount[EVENT]).toBe(1); + expect(first.$$listenerCount[EVENT]).toBeUndefined(); + + $rootScope.$broadcast(EVENT); + expect(spy.callCount).toBe(1); + })); }); @@ -1091,29 +1113,78 @@ describe('Scope', function() { })); - it('should return a function that deregisters the listener', inject(function($rootScope) { - var log = '', - child = $rootScope.$new(), - listenerRemove; - - function eventFn() { - log += 'X'; - } + it('should increment ancestor $$listenerCount entries', inject(function($rootScope) { + var child1 = $rootScope.$new(), + child2 = child1.$new(), + spy = jasmine.createSpy(); - listenerRemove = child.$on('abc', eventFn); - expect(log).toEqual(''); - expect(listenerRemove).toBeDefined(); + $rootScope.$on('event1', spy); + expect($rootScope.$$listenerCount).toEqual({event1: 1}); - child.$emit('abc'); - child.$broadcast('abc'); - expect(log).toEqual('XX'); + child1.$on('event1', spy); + expect($rootScope.$$listenerCount).toEqual({event1: 2}); + expect(child1.$$listenerCount).toEqual({event1: 1}); - log = ''; - listenerRemove(); - child.$emit('abc'); - child.$broadcast('abc'); - expect(log).toEqual(''); + child2.$on('event2', spy); + expect($rootScope.$$listenerCount).toEqual({event1: 2, event2: 1}); + expect(child1.$$listenerCount).toEqual({event1: 1, event2: 1}); + expect(child2.$$listenerCount).toEqual({event2: 1}); })); + + + describe('deregistration', function() { + + it('should return a function that deregisters the listener', inject(function($rootScope) { + var log = '', + child = $rootScope.$new(), + listenerRemove; + + function eventFn() { + log += 'X'; + } + + listenerRemove = child.$on('abc', eventFn); + expect(log).toEqual(''); + expect(listenerRemove).toBeDefined(); + + child.$emit('abc'); + child.$broadcast('abc'); + expect(log).toEqual('XX'); + expect($rootScope.$$listenerCount['abc']).toBe(1); + + log = ''; + listenerRemove(); + child.$emit('abc'); + child.$broadcast('abc'); + expect(log).toEqual(''); + expect($rootScope.$$listenerCount['abc']).toBeUndefined(); + })); + + + it('should decrement ancestor $$listenerCount entries', inject(function($rootScope) { + var child1 = $rootScope.$new(), + child2 = child1.$new(), + spy = jasmine.createSpy(); + + $rootScope.$on('event1', spy); + expect($rootScope.$$listenerCount).toEqual({event1: 1}); + + child1.$on('event1', spy); + expect($rootScope.$$listenerCount).toEqual({event1: 2}); + expect(child1.$$listenerCount).toEqual({event1: 1}); + + var deregisterEvent2Listener = child2.$on('event2', spy); + expect($rootScope.$$listenerCount).toEqual({event1: 2, event2: 1}); + expect(child1.$$listenerCount).toEqual({event1: 1, event2: 1}); + expect(child2.$$listenerCount).toEqual({event2: 1}); + + deregisterEvent2Listener(); + + expect($rootScope.$$listenerCount).toEqual({event1: 2}); + expect(child1.$$listenerCount).toEqual({event1: 1}); + expect(child2.$$listenerCount).toEqual({}); + })) + }); }); @@ -1360,6 +1431,23 @@ describe('Scope', function() { })); + it('should not descend past scopes with a $$listerCount of 0 or undefined', + inject(function($rootScope) { + var EVENT = 'fooEvent', + spy = jasmine.createSpy('listener'); + + // Precondition: There should be no listeners for fooEvent. + expect($rootScope.$$listenerCount[EVENT]).toBeUndefined(); + + // Add a spy listener to a child scope. + $rootScope.$$childHead.$$listeners[EVENT] = [spy]; + + // $rootScope's count for 'fooEvent' is undefined, so spy should not be called. + $rootScope.$broadcast(EVENT); + expect(spy).not.toHaveBeenCalled(); + })); + + it('should return event object', function() { var result = child1.$broadcast('some'); -- cgit v1.2.3