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

feat($rootScope): adds $onRootScope method #5507

Closed
wants to merge 1 commit into from

Conversation

cburgdorf
Copy link
Contributor

This adds an $onRootScope method to the
scope type. This allows developers to use
$rootScope.$emit + $scope.$onRootScope as
a fast eventBus which doesn't use scope
bubbleing.

Fixes #4574, Relates to #5371

This adds an $onRootScope method to the
scope type. This allows developers to use
$rootScope.$emit + $scope.$onRootScope as
a fast eventBus which doesn't use scope
bubbleing.

Fixes angular#4574, Relates to angular#5371
@cburgdorf
Copy link
Contributor Author

@IgorMinar just to lower the barrier to include that for 1.3 I prepared this PR with tests included. The only thing I'm not 100% sure about is the documentation update (the wording and the link to StackOverflow)

@@ -1116,6 +1116,72 @@ describe('Scope', function() {
}));
});

describe('$onRootScope', function() {

it('should add listener for both $emit and $broadcast events that are triggered on $rootScope', inject(function($rootScope) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there isn't really a distinction between $emit and $broadcast events, so this confuses me... I don't think it's terrible to test both, but the name just seems a bit more verbose than it needs to be (it is just my opinion, though)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you are right. I basically just copied over the tests for $on to make the tests look more or less the same. I'm happy to change it if I should. But it should then be changed for the $on tests as well.

@ghost ghost assigned tbosch Dec 28, 2013
@IgorMinar
Copy link
Contributor

There is one alternative to this change which we should consider. In AngularDart we implemented all watches and event listeners as a simple linear list and we keep metadata about the boundaries for the items in the list as demarcated by scopes (all watches/listeners for a given scope are grouped together).

This means that when the listeners need to be called we just iterate over the list and call them in order and when a scope is destroyed we find the segment in the list and relink it.

This gives you the same perf benefits as $onRootScope without the extra api and putting the responsibility on thinking about which api to use on the developer.

@cburgdorf
Copy link
Contributor Author

Not sure I fully get the implementation yet but if what you propose can make such claims than I'm all for it:

  • a $rootScope.$broadcast does not walk down the scope tree
  • the number of function invocations $rootScope.$broadcast causes has a 1:1 relationship to the number of event consumers
  • it is guaranteed that fn in $scope.$on('foo', fn) will only fire if it truly is a child of a parent scope which broadcasted the event (not speaking about $rootScope.$broadcast here as every scope is a child of $rootScope)

If the above holds true for your proposed the solution than yep I'm all for it!

@kasajian
Copy link

kasajian commented May 7, 2014

I just wanted to let you guys know that I updated cburgdorf's jsperf and the problem is no longer there in the latest angular.js as of this writing (1.2.16) (probably fixed earlier). The jsperf was referencing an older Angular. Although the trick of using $rootScope.emit() is actually quite clever trick, it has side-effects, and is no longer necessary. The updated jsperf shows the problem is no longer there: http://jsperf.com/rootscope-emit-vs-rootscope-broadcast/27

By the way, when I say it has side-effects, what I mean is -- there are places where it's better to listen using $scope.on(). $that $scope object will go away if it's associated with a controller referenced from a view when the user changes pages. When the $scope is destroyed, anything it's listening on is unsubscribed. However, if every controller listens using $rootScope.$on(), then that subscription will stay in $rootScope, and will accumulate. You'll have to listen on the $destroy event of $scope and then manually unsubscribe using the original return value of $rootScope.on(). Thankfully, that type of processing is no longer necessary.

@cburgdorf
Copy link
Contributor Author

@kasajian just a note here: I never suggested to use $rootScope.$on from within a controller exactly because then you would not deregister the event listener. What I suggested was to use $scope.$onRootScope because that one automatically deregisters the handler when the $scope is destroyed.

@petebacondarwin petebacondarwin modified the milestones: 1.3.0, Backlog Jul 14, 2014
@petebacondarwin petebacondarwin self-assigned this Jul 14, 2014
@petebacondarwin petebacondarwin modified the milestones: 1.3.0-beta.16, 1.3.0 Jul 14, 2014
@IgorMinar
Copy link
Contributor

In the light of all the performance improvements we've done since this issue was created, does it still make sense to add an api like $onRootScope?

I'm thinking that the answer is no. Anyone disagrees? @cburgdorf?

@cburgdorf
Copy link
Contributor Author

Nope, should be fine now.

@cburgdorf cburgdorf closed this Jul 15, 2014
@maximvl
Copy link

maximvl commented Jul 15, 2014

@IgorMinar could you describe what improvements affect this?

@WhatFreshHellIsThis
Copy link

Can someone please clarify this, originally we had this issue: #4574

However it was closed and "the discussion" was moved to this issue. Now this issue is closed but as far as I can tell from reading through it nothing has actually changed except that there might be some improvement in the original messaging system that will make it faster but I'm not sure if that relates to all the issues people were experiencing entirely such as unregistering being tricky etc.

Many of us are using workarounds like making our own messaging service like this one:
https://gist.github.com/turtlemonvh/10686980

So what is now the guidance for the original problem of using Angulars event system as an event bus? Or is it in a state of flux and not actually recoded to address usage as an event bus and we should stick with our workarounds?

@cburgdorf
Copy link
Contributor Author

@WhatFreshHellIsThis The way I see it you can now safely use $broadcast without the performance penalty because it doesn't walk down the entire scope tree anymore. This means you can just use the builtin event mechanism as your main event bus.

var unsubscribe = this.$root.$on(name, listener);
this.$on('$destroy', unsubscribe);

return unsubscribe;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we unsubscribe both listeners? root.$on & this.$on

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide $scope.$onRootScope method
9 participants