I have extended my entities, which are provided by breezejs, with properties that are the result of a recursive call to the property. The property is display only and I never want to store the value.
In particular I added a "start" and an "end" property which depends on the "end" date of the entity before it and the timespan of the current entity. These values are deterministic and there is no sense in storing them. This works brilliantly until I have a few thousand entities. At this point performance tanks due to the two way binding as a result of all of the properties that were touched during the calculation.
I have a caching scheme in place and simply want angularjs to do nothing during the calculation of the "start" and "end" properties.
How can I prevent any watchers/two way binding from occurring while I am calculating these properties?
In a broader sense, how can you work with large amounts of data within angularjs without drowning in watchers?
Edit:
Something like this appears to be the solution:
https://coderwall.com/p/d_aisq/speeding-up-angularjs-s-digest-loop
Where the watchers in the scope are suspended during an operation and then resumed after I am done.
var watches = scope.$$watchers;
scope.$$watchers = [];
// do fast computation that touches a lot of data
scope.$$watchers = watchers;
Edit 2:
This is before binding to the view, this is in assembly data for binding. I don't think one time bindings can be done in the controller?
Like #zeroflagL's suggestion, one time binding is an option that's available
An expression that starts with :: is considered a one-time expression. One-time expressions will stop recalculating once they are stable, which happens after the first digest if the expression result is a non-undefined value
Expressions # angularjs.org
Also, if you want to start a calculation that won't trigger a digest cycle, don't change any members on the $scope. Just use "var" and in the end of the process do $scope.data = newValue;
Related
While profiling Angularjs app I can see that on a slow page digest runs 5 times and many expressions evaluated too many times and many expensive controller functions are called multiple times as well.
Is there a way to prevent watched expression from being evaluated more than 1 time within a single digest?
Few examples to better illustrate the question:
I have an icon picker with 300+ icons, each icon has {background: colorPicker.color}
I have a very expensive function to check if page is modified and needs saving. It does a deep equals on initial version and current version of a very large object.
So I know that when digest is running I do not need to re-evaluate these.
I would like to have a way to cache calculated values within a single digest cycle.
One time binding is not an option, these watched expressions do change, just not within single digest.
I'm using latest AngluarJS release,
I was wondering if I can combine one time with one way binding in one expression inside ng-if directive , some like that:
ng-if="(vm.isUnix) && (::vm.isGnsEnabled)"
The line above throws an error, not working
This is not possible. The one time binding token must come first and will mean that once the expression is stable it will no longer be watched.
ng-if="::vm.isUnix && vm.isGnsEnabled"
If vm.isUnix can change during the lifetime of your component and you need to reflect this change in the view, there is no way to prevent a watcher for this expression.
Edit: basically one-time binding is for the entire expression, not for individual properties inside the expression.
When reading about one-way binding in angular.component style, I came across multiple statements that < vs = produces less watchers (= will have additional watcher to propagate value change from child to parent).
However I've just created a dummy component, passed the object to it via = and < and number of watchers is the same.
So speaking strictly about performance: are there any diffirence between < and = ?
I came here with the same question, and was disappointed to see no answer...
I have a small test application where I use all kinds of binding, three of them being one-way. I observed the number of watches (using ng-stats utility), got 42.
I changed these "<" to "=", which changed the behavior of my app accordingly, of course. ng-stats still reported 42 watches.
So, at least in terms of watches, this brings no performance improvement.
I guess it is here more as a convenience, avoiding unwanted side effects (child changing a value, parent's value being changed unwillingly) and promoting good practices (using bound functions instead of watches, as explained below).
It can still be a way to avoid watches: a common practice when a parent wants to be advised of changes in a child it is to use two-way binding, set up a watch on the value, and react on changes.
An alternative is to set up a one-way binding (to feed the child), and to provide a callback (via a "&" binding), and thus to let the child to notify of changes via this callback.
It is more proactive and it removes a watch.
I'm currently working on the performance of an angularJS application, and was wondering if there is any difference in performance between these two examples:
http://plnkr.co/edit/T5aHybkkCoF5MDzXX2Kk?p=preview
http://plnkr.co/edit/sX2ffPPOQTyFbb70elwp?p=preview
The difference is that in the first example, in the file 'testdirective.html' the ng-if boolean is not bound in a one time expression. However, it is already bound as a one-time binding in the 'index.html'.
<div ng-if="enabled">
vs
<div ng-if="::enabled">
Is using a one-time expression in the directive as well as in the index file better for the performance?
Thanks!
Yes, there will be a performance difference (although negligible in this case).
The directive doesn't take into account if the value passed to it was one-time bound or not, so if the directive's template doesn't use a one-time binding it will register a new watcher.
The cost of running this watcher during the digest cycle will bascially be the difference in performance in the two examples.
An extra note on your example that doesn't really touch the question is that the following two cases will behave the same:
<testdirective enabled="::true"></testdirective>
And
<testdirective enabled="true"></testdirective>
In the second case true will be treated as a constant that cannot change and the registered watcher will be removed, just like with a one-time binding.
I understand that both scope watchers and filters are executed repetitively within the digest loop. However the following is a bit unclear to me:
Are they executed the same amount of cycles?
Are both triggered by the same circumstances?
Watches are checked every digest cycle. And a $watch can watch an Angular expression. Inside an expression there can be a filter which Angular must evaluate (using $interpolate) to decide if the watched expression has changed.
Are both triggered by the same circumstances?
So, filters aren't run directly by $digest but are rather run as a consequence of a $watch. In effect, filters are triggered by the evaluation of watched expressions.
There's a few reasons filters get talked about specially with regard to the performance of $digest. Looking at these two expressions:
Expression 1: {{searchText+2}}
Expression 2: {{searchText | myFilter:true}}
Both expressions will be evaluated everytime a $digest is triggered. So one risk area is simply that myFilter could be complex and end up using a lot more processor cycles than something simple (like the above +2).
A bit less obviously, a filter can cause extra runs of all watchers if it's not idempotent. On each run of $digest a dirty bit is set if any of the watches results in a change. And it reruns all the watches again if that dirty bit is set. This allows any changes to be propagated. For instance if searchText is changed by one of the watches then Angular needs to give all the other watchers a chance to see if their results depend on searchText and thus should change.
Herein lies the difference between expressions 1 and 2 above. As a worst case scenario imagine myFilter returns a random number. $digest runs, sees a change when evaluating the watches (the previous result of the filter doesn't match the new result) so it runs through the watch list again. Because the filter returns a random number it's results have very likely changed, triggering yet another run of $digest. etc, etc... Angular has a built in stop after 10 loops through it's watch list- it figures if the results don't stabilize after 10 tries then something is wrong and so it gives up throwing "Error: 10 $digest() iterations reached. Aborting!"
Are they executed the same amount of cycles?
Therefore at a minimum each watched expression (including any filters) is run twice per $digest. Once because of the change that triggered $digest and once more to check if that result needs to propagate. If that propagation resulted in any changes then the watches and filters will be run again until nothing changes.
And, of course, if multiple watched expressions use the same filter then that filter will be run, following the above, for each expression.