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 $watchedmy 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?
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.
Related
enter image description here
height value change by setHeight() but
page value does not change by setPage()
when i use annotation code, page value change...
Rather than assuming react's useState() hook is not updating its value when you call the setter function, it's probably safer to assume your surrounding logic is keeping setPage() from being called. I would start by adding a console.log statement above each setPage call to verify whether that part of the code is being reached or not.
If the code is not being reached, then you need to revisit your logic. It looks like page increase will not be reached if:
$html.is(":animated")
e.originalEvent.deltaY <= 0
page == 6
Is that your intended logic? Based on your indentation after the line if($html.is(":animated")) return; I'm a little concerned you meant to do if($html.is(":animated")) { there instead. I would also suggest you might want to get out of the habit of omitting the brackets on single line if statements, this can really bite you sometimes, and may be what is causing your logic to not work as intended here.
This question already has answers here:
angularjs ng-if difference between value and function
(2 answers)
Closed 7 years ago.
Is it a bad practice to use the return value of a function call inside ng-if. Will it affect the digest cycle/performance?
As any expression used in the view, it will be reevaluated at each digest cycle to see if its value has changed or not, and thus if the element must be removed or added to the DOM.
If the function is simple enough, no problem. If it tries to compute the 10000th decimal of pi, you'll have performance problems. What matters is to understand what you're doing, and how it works.
So if you feel using a function makes your code simpler, go for it, and start optimizing if you really need to.
No, that's just fine. The function shouldn't have side effects (especially not change other fields taking part in change detection)
Function calls will be slightly slower than just pure evaluation of a variable on scope due primarily to the fact that the step of "run function" must be handled before "check truthiness" is run on either. But this performance impact depends on how long your function takes to run.
Generally, anything that constitutes complex logic can be extracted out into a function and it will clean up your HTML since you won't need long JS in your template file.
I often extract to a function if the statement cannot be done in a single JS statement or if the statement starts being longer than about 40 characters.
To make a long story short, in our application at work, we have a function that creates multiple objects that inherit methods from a prototype.
As such:
function MileCounter(totalMilesRan, numOfDaysToRunThem) {
this.totalMilesRan = totalMilesRan;
this.numOfDaysToRunThem = numOfDaysToRunThem;
};
MileCounter.prototype.avgMilesPerDay = function() {
return (this.totalMilesRan/this.numOfDaysToRunThem);
}
And then in the view, this is called like this:
<div> {{mileObj.avgMilesPerDay()}} </div>
The disagreement comes from their belief that the average should be provided to the mileObj in the controller so the average can be called in the view just as they would to get moneyObj.totalMilesRun as:
<div> {{mileObj.avgMilesPerDay}} </div>
Something to keep in mind is that the actual objects in question have many more properties than just two and the number of objects being created is usually in the dozens but could eventually sky rocket into the hundreds or even thousands.
My Coworkers believe that the view should not be concerned at all with calculating data and should only be concerned with displaying it.
My Question: Is there an objective reason why it would be better to add the avgMilesPerDay value directly to each object, rather than just calling a prototype method to handle it? It is my understanding that adding a bunch of properties to objects could eventually be a drag on memory when there are enough objects being created, with enough properties on each one, and that having simple prototype methods could help ease that burden.
Advantages of calling a function:
less memory usage
more encapsulation: changing the value of totalMilesRan or numOfDaysToRunThem automatically changes the value of the average
Advantages of adding a field for the average:
slightly more efficient: the average doesn't need to be computed again and again
I would keep using the function from the view unless you have a performance problem, and have proven that it comes from the function call, and it can't be solved in another way (like one-time binding for example).
While recently developing an AngularJS app I came across performance issues related to watches, digging down further makes me wonder what is the time complexity for the dirty checking algorithm using two-way data binding? From the looks of it, after every event there's a digest cycle, which inturn calls a watch cycle, so this must be O(n^2)... is this correct?
The dirty checking happens in the $diggest cycle, so we need to study the complexity of the $diggest cycle.
The $diggest cycle is the stage in which Angular ensures the changes of the model have settled,
so that it can render the view with the updated changes. In order to do that,
Angular starts a loop in which each iteration evaluates all the template expressions
of the view, as well as the $watcher functions of the $scope.
If in the current iteration the result is the same as the previous one,
then Angular will exit the loop. Otherwise, it will try again.
If after 10 attempts things haven't settled, Angular will exit
with an error: The "Infite $diggest Loop Error" (infdig).
Therefore the complexity of the dirty checking will deppend on the complexity of the functions that you have in the $watchers and in the expressions of your view (like your $filters).
So the $diggest cycle alone has a complexity of O(n) where n is the number o $watchers and template expressions that need to be iterated. But then you will have to consider the complexity of those functions. Also, notice that the $diggest cycle will always visit all those functions at least twice (in order to make sure that the changes have settled), so if you want to be more precise you could say that it has a complexity of at least O(n*2) and a maximum complexity of O(n*10), but since 2 is a constant value which its max number is 10 the complexity should still be considered O(n).
I'm trying to use ng-grid with a sliding window of 100 records. The data is coming in realtime via signalR and every message trigger the following method:
onNewTrades(records) {
console.log("onNewRecord", records);
if (connectionStopped) return;
for (var i = 0; i < records.length; i++) {
if ($scope.recordsData.length > maxRecordsInTable)
$scope.recordsData.pop();
$scope.recordsData.unshift({
t: new Date(records[i][0]),
p: records[i][1],
a: records[i][2]
});
}
}
I have a threshold of 100 maxRecordsInTable before I start popping items off the end (before adding the new message to the front)
However, when it reaches my threshold the table simple stops updating. Strangely though, if I set a breakpoint on unshift(), the table does update with every "continue".
I suspect it's some kind of angular timing issue? I tried using $timeout()
Or may when I pop() and unshift() at the same time it doesn't pick up a change in the array? I tried using $apply() (error already in digest cycle)
There are a few things that could be happening here.
First of all, if onNewTrade is using an external, non-angular, library making xhr requests outside of angular's framework (i.e. not using $http or $resource), you have to call $scope.$apply(function(){ }) around the code you want the scope's digest to know about. That part's not clear from what you've provided. edit: Read more about when to use $scope.$apply
Second, angular's digest phase does a minimum of two passes (first to make changes, second to make sure there are no more changes). It does at most 10 passes by default. If angular evaluates the scope 10 times and it is not consistent, it gives up. see documentation. It does this because you can have multiple watch functions where one watch affects the scope higher in the hierarchy, which makes changes and affects the same watch.. basically causing an evented infinite loop. Do you see a console error about '10 $digest iterations, aborting!' or something similar?
There are a couple of other questionable things:
is onNewRecord asynchronous? If so, I would doubt connectionStopped is being done correctly. You could be returning early. Because you say a breakpoint shows values on unshift, its probably not the cause of this issue (and most likely missing $scope.$apply is the problem), but I'd rethink this code.
Your function is onNewTrade(records), but you log onNewRecord(record). If you have nested variables here, make sure you haven't excluded code that may contain typos (e.g. record instead of records). You might be working on an unexpected object.