Reputation: 891
I have multiply functions like count()
getTotal()
. All those calculation functions have to run if something changes my cart/object
.
Currently I'm using them in the view like {{count()}}
. So all those functions are running multiply times.
I think those functions are called multiply times, because of the dirty checking. Is this correct?
Now I read about the $watch
function and that you should try to avoid it, because of performance issues. Anyway I wanted to test it, and $watched
my cart
. I found out it only logs a single time so wouldn't it be faster to $watch
my cart
and call all the calculations functions in there and then just bind to the result of the calculations?
I'm a bit confused know, cause i thought it would have the same behaviour. Because the $watch
will also go through the digest loop.
I know there are already a lot of similiar questions, but having somebody explaining it directly for my case in a not so hard english, would make it a lot easier.
Example: What has better performance? and why? Does both variants go through the digest cycle? Why then Variant 2 logs 10 times and Variant 1 only 1 time?
Variant 1
// bind these to scope and show like {{totalGross}}
cartService.totalGross = null;
cartService.totalNet = null;
cartService.totalItems = null;
// watch the cart here and update all variables
$rootScope.$watch(function () {
return cartService.cart;
}, function(){
cartService.totalGross = cartService.getCartTotalGross();
cartService.totalNet = cartService.getCartTotalNet();
cartService.totalItems = cartService.getTotalItems();
}, true);
Variant 2
// bind these to scope and show like {{getCartTotalGross()}}
cartService.getCartTotalGross();
cartService.getCartTotalNet();
cartService.getTotalItems();
Trying to answer my own question, altough i'm not shure if it's correct.
Variant 1 you have 1 more watcher, but we just watch values instead of functions and the 1 more because we watch the cart manually with $watch
. But the watchers are less heavy to compute.
Variant 2 we are watching the value that functions are returning, so the functions have to calculate the same value multiply times, witch is more heavy. In each digest Cycle the UI get updated
Is this correct? So Variant 1 is better for performance?
Upvotes: 2
Views: 357
Reputation: 691755
It's generally considered bad practice to use $watch not (only) because it has performance issues, but because there are, most of the time, better alternatives (both clearer, and more efficient).
For example, instead of watching a model value that changes when the user enters something in an input, in order to compute something that depends on this value, you can use ng-change on the input and call the computation from there. This is faster, and the intent is clearer.
Now, to answer your question:
Every time an event is handled by angular, the event handler can modify anything in the scope. Angular has no way to know what has been modified. So it has to call, for example, getTotalItems()
because an event handler might have changed something that makes the returned value of getTotalItems()
change.
Additionally, changing any value that is watched causes a watcher function to be executed, and this watcher function can, in turn, change other values, that can be watched by other watchers, etc. etc. So angular needs to evaluate all the watched expressions in a loop, until it can be sure that the last evaluation leads to the same results as the previous one. That's what is known as the digest loop.
So, in short, having {{ getTotalItems() }}
in the view is not a big deal if that function just returns something simple, like the length of an array, or the sum of a few values. But if it computes the meaning of life, it's a really bad idea, and you should instead compute the meaning of life only when it needs to be computed, and store the result in a variable that is displayed in the view.
You can do that with a watcher, but that should be the last resort. As I said already, there are often better alternatives.
Upvotes: 3