Why to use $emit now that $broadcast overperforms it? - angularjs

Now that $broadcast is almost 7x faster than $emit, there are reasons to use $emit instead of only to use $rootScope.$broadcast?
Why is not good to use $rootScope in all your controllers now that $scope use is transparent, except to handle events?
Am I totally wrong with that concept?

There are a couple things wrong with this situation. Using $rootScope as an event bus can work, but it's not a simple black and white performance decision.
Changes to $broadcast in Angular 1.3.0-beta19 allow for $broadcast to exit early if nobody is listening to the given event. This doesn't automatically mean that $broadcast is more efficient than $emit, only that it doesn't run with no listeners, while $emit still will. This makes it appear to be faster, in the benchmark you reference in your comments.
$broadcast dispatches events downward from the $scope to all child scopes (and their children). With even one listener, $broadcast from $rootScope is going to hit every scope in your application.
$emit, on the other hand, bubbles events upwards. Since $rootScope is the topmost scope, $emit on $rootScope will always touch exactly one scope. If you MUST use $rootScope, $emit will be better.
Using $rootScope has it's own problems, however; If you register a listener from a controller, you will need to manually remove the listener when your controller is destroyed. Controllers are not singletons, and multiple controllers which create a listener and then are destroyed without removing the listener will eventually create a memory leak in your app.
Using a global event bus can also be a problem, as it's relatively easy for multiple components to try to register the same event, or trigger an event that they aren't responsible for, creating subtle bugs that can be very difficult to find.
In general, it's best to use $broadcast from a scope to it's descendants as close to the bottom of the tree as possible. Only services should use $rootScope, as they do not have access to other scopes. In that case, $emit is better, as it reduces the number of scopes touched to 1. However, Almost all cases of cross controller communication can be better managed with correct scope hierarchies that rely on bindings rather than events.

Related

which one is a better solution $rootScope.$emit or $scope.$broadcast? [duplicate]

This question already has answers here:
$rootScope.$broadcast vs. $scope.$emit
(6 answers)
Closed 7 years ago.
I am trying to trigger an event from a parent controller and the child controller should listen to it.Using angular's event framework there are two ways to go
Method 1
parentcontroller
$scope.$broadcast("eventToChild");
childController
$scope.$on("eventToChild",function({console.log("received event");})
Method 2
parentcontroller
$rootScope.$emit("eventToChild");
childController
$rootScope.$on("eventToChild",function({console.log("received event");})
I understand that $rootscope.$emit will only invoke listeners on $rootscope and we can stop the propagation of the event using event.stopPropagation(),given this fact how can $scope.$broadcast be a better solution than $rootScope.$emit.
Which one among these two is a better solution and why?
There's no better performance differences between them. But the difference is just of it's usage.
$broadcast is used for broadcasting event to downwards and $emit is used for emitting the event upwards.
Next thing, you're wrong at saying $emit works only with $rootScope. It works with $scope too.
For more information about them see differences here.
They just work in a different way.
Broadcast goes from the element downards, to whoever is trying to catch an event; in the case of rootscope, every single scope is below it, thus everyone can catch it and treat it; While emit is the way of a child scope to go up. Usually, if you want to trigger the event in a condition/scope parameters that are related to the parent, you'll use broadcast - while if you want to use it with those of the child, you'll use emit.

How to detect variable change events between controllers

I'm making a fairly trivial notification system where I want a main menu (outside ng-view) to display the last activity of the user, from what I've read it looks like a service is best used to communicate between controllers. How should I detect when something happens in controller A immediately from controller B. Should I $watch the variable of the service? Or is there an easier way to do this? Or given should I just have a root scope variable and $watch and change that with the controllers?
You should use $broadcast and $on methods. Here's a good example found on the internet
Also, there is no more an issue with $broadcast over $emit in recent versions of angular, because $broadcast runs as fast as $emit.
Don't use $watch for this because it will create a lot of events ( angular has also a limit of 10 rerun iterations to prevent deadlock ).
The watch listener may change the model, which may trigger other listeners to fire.
This is achieved by rerunning the watchers until no changes are
detected. The rerun iteration limit is 10 to prevent an infinite loop
deadlock.
Read more here
$watch will trigger everytime your variable change. Your can use $emit or $broadcast with $on for a less regular event.
This can be done in a factory : how to emit events from a factory
Could store something in the global $window and create a watch expression on it, that is just off the top off my head, might be a better way to do it.
Also take a look at this as it is like the sort of thing you need to do:
Share data between AngularJS controllers

Are $scope watchers and listeners automatically removed when $scope is destroyed

I'm just wondering if any $scope.$watch/$scope.$watchCollection/$scope.$on are automatically deregistered when the $destroy event is called on the $scope. I see that they all return a deregister function and I'm wondering if it's good practive to call those functions on a $scope destroy event or will Angular do that itself. I do it for performance reasons right now, but if this happens automatically then I will stop calling those functions directly.
If you are listening for $destroy event it means that your $scope is going to be destroyed so all $watches will be removed automatically so you don't need to, that's why it's always better to register $watches at current $scope and not at $rootScope (since those will persist as long as the app is running or you deregister manually). Same thing If you are calling the $destroy event/method yourself.

Why do we use $rootScope.$broadcast in AngularJS?

Tried to find some basic information for AngularJS $rootScope.$broadcast, But the AngularJS documentation doesn't help much. In easy words why do we use this?
Also, inside John Papa's Hot Towel template there is a custom function in the common module named $broadcast:
function $broadcast() {
return $rootScope.$broadcast.apply($rootScope, arguments);
}
I did not understand what this is doing. So here are couple of basic questions:
1) What does $rootScope.$broadcast do?
2) What is the difference between $rootScope.$broadcast and $rootScope.$broadcast.apply?
$rootScope basically functions as an event listener and dispatcher.
To answer the question of how it is used, it used in conjunction with rootScope.$on;
$rootScope.$broadcast("hi");
$rootScope.$on("hi", function(){
//do something
});
However, it is a bad practice to use $rootScope as your own app's general event service, since you will quickly end up in a situation where every app depends on $rootScope, and you do not know what components are listening to what events.
The best practice is to create a service for each custom event you want to listen to or broadcast.
.service("hiEventService",function($rootScope) {
this.broadcast = function() {$rootScope.$broadcast("hi")}
this.listen = function(callback) {$rootScope.$on("hi",callback)}
})
What does $rootScope.$broadcast do?
$rootScope.$broadcast is sending an event through the application scope.
Any children scope of that app can catch it using a simple: $scope.$on().
It is especially useful to send events when you want to reach a scope that is not a direct parent (A branch of a parent for example)
!!! One thing to not do however is to use $rootScope.$on from a controller. $rootScope is the application, when your controller is destroyed that event listener will still exist, and when your controller will be created again, it will just pile up more event listeners. (So one broadcast will be caught multiple times). Use $scope.$on() instead, and the listeners will also get destroyed.
What is the difference between $rootScope.$broadcast & $rootScope.$broadcast.apply?
Sometimes you have to use apply(), especially when working with directives and other JS libraries. However since I don't know that code base, I wouldn't be able to tell if that's the case here.
$rootScope.$broadcast is a convenient way to raise a "global" event which all child scopes can listen for. You only need to use $rootScope to broadcast the message, since all the descendant scopes can listen for it.
The root scope broadcasts the event:
$rootScope.$broadcast("myEvent");
Any child Scope can listen for the event:
$scope.$on("myEvent",function () {console.log('my event occurred');} );
Why we use $rootScope.$broadcast? You can use $watch to listen for variable changes and execute functions when the variable state changes. However, in some cases, you simply want to raise an event that other parts of the application can listen for, regardless of any change in scope variable state. This is when $broadcast is helpful.
Passing data !!!
I wonder why no one mention that $broadcast accept a parameter where you can pass an Object
Example:
// the object to transfert
var obj = {
status : 10
}
$rootScope.$broadcast('status_updated', obj);
$scope.$on('status_updated', function(event, obj){
console.log(obj.status); // 10
})
What does $rootScope.$broadcast do?
It broadcasts the message to respective listeners all over the angular app, a very powerful means to transfer messages to scopes at different hierarchical level(be it parent , child or siblings)
Similarly, we have $rootScope.$emit, the only difference is the former is also caught by $scope.$on while the latter is caught by only $rootScope.$on .
refer for examples :- http://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

angularJS $on event handler trigger order

I was wondering two things, in the context of angularJS event handling.
How is defined the order in which handlers listening to the same event are triggered?
Is it a sign of a bad design if you start wondering about this?
After reading documentation on angular $on, $broadcast and $emit as well as native DOM event flow I think I understand in which order event handlers will be trigger in different scopes. The problem is when several handlers listen in the same scope ($rootScope for example) from various places (Controllers vs Services for example).
To illustrate the problem I have put together a jsfiddle with one controller and two services, all communicating through $rootScope http://jsfiddle.net/Z84tX/
Thanks
Very good question.
Event handlers are executed in order of initialization.
I haven't really thought about this before, because my handlers never needed to know which one run first, but by the look of you fiddle I can see that the handlers are called in the same order in which they are initialized.
In you fiddle you have a controller controllerA which depends on two services, ServiceA and ServiceB:
myModule
.controller('ControllerA',
[
'$scope',
'$rootScope',
'ServiceA',
'ServiceB',
function($scope, $rootScope, ServiceA, ServiceB) {...}
]
);
Both services and the controller define an event listener.
Now, all dependencies need to be resolved before being injected, which means that both services will be initialized before being injected into the controller. Thus, handlers defined in the services will be called first, because service factories are initialized before controller.
Then, you may also observe that the services are initialized in order they are injected. So ServiceA is initialized before ServiceB because they are injected in that order into the controller. If you changed their order inside the controller signature you'll see that their initilization order is also changed (ServiceB comes before ServiceA).
So, after the services are initialized, the controller gets initialized as well, and with it, the event handler defined within.
So, the end result is, on $broadcast, the handlers will be executed in this order: ServiceA handler, ServiceB handler, ControllerA handler.
It's a bit messy (and I wouldn't recommend it) to go about this route, but wanted to provide an alternative incase you are unable to ensure serviceB will be initialized before serviceA and you absolutely need serviceB's listener to be executed first.
You can manipulate the $rootScope.$$listeners array to put serviceB's listener first.
Something like this would work when adding the listener to $rootScope on serviceB:
var listener, listenersArray;
$rootScope.$on('$stateChangeStart', someFunction);
listenersArray = $rootScope.$$listeners.$stateChangeStart;
listener = listenersArray[listenersArray.length - 1];
listenersArray.splice(listenersArray.length - 1, 1);
listenersArray.unshift(listener);
To provide an answer to #2 (because I think #Stewie's answer to #1 is a really good one), while I'd hesitate to ever offer conclusive rules that say, "if you see this, then it's bad code", I would offer to say that if you have two event handlers, and one can only execute after the other has run: you should evaluate why that is the case and if you couldn't better encapsulate or organize the way your logic executes.
One of the primary use cases of pub/sub event broadcasting/listening is to allow separate components that are fully independent of one another to operate on their domain of influence in an independent way asynchronously. By one handler having to operate only after another handler has run first you are removing the asynchronous nature of pub/sub by adding a secondary requirement (though possibly necessary).
If it's an absolutely necessary dependency, then no: it's not a symptom of bad design - its a symptom of the requirements of that feature.
I'm a new user to Angular JS so please forgive if the answer is less than optimal. :P
If you require the order of the functions triggered by an event to be dependent (i.e., Function A then Function B) then might not creating a trigger function be better?
function trigger(event,data) {
FunctionA(event,data);
FunctionB(event,data);
}
$rootScope.on('eventTrigger',trigger);
To add another comment on point #2, if you need to guarantee order you could implement an observer pattern using a service with an array of listeners. In the service you can define "addListener" functions that also defines how the listeners are ordered. You can then inject this service into whatever other components need to fire events.
It is a bit hacky and absolutely not recommended to use in your design, but sometimes you don't have a choice (been there so many times).
$rootScope.$on('someEvent', () => {
setTimeout(eventHandler1, 1);
});
$rootScope.$on('someEvent', eventHandler2);
const eventHandler1 = () => {
console.log('This one runs last');
};
const eventHandler2 = () => {
console.log('This one runs first');
};
As you can see from the example, I have used setTimeout to trick the order of running the actual handler and make the eventHandler1 handler to run last, although it has been called first.
To set the execution priority, just change the setTimeout delay as necessary.
This is not ideal and is only suited for specific cases.

Resources