I have tried all three terms for refreshing the scope after triggering any event. Sometimes I got an error
$apply or $digest is already in progress.
Finally I am using $timeout without any error. But I don't know that which one is best as per the complexity or performance.
$timeout calls $apply which calls $digest.
In more detail:
$apply triggers a $digest cycle on root scope (therefore on all scopes within the application).
$digest can be called directly on a particular child scope instead of on root -- but watchers on parent or sibling scopes won't be triggered, so incautious use of $digest can leave different scopes visibly out of synch.
$timeout waits for the next digest cycle, and then calls $apply (and thus is basically a simple way to call $apply without worrying about "digest already in progress" errors.) The newer $evalAsync is similar but applies to the current digest if possible, so is sometimes slightly more performant than $timeout. (There is also $applyAsync which is as far as I can tell basically $timeout with a 10ms delay, for reasons which I'm sure make sense to someone.)
TL;DR: leave Angular to manage its own data lifecycle as much as possible. When you need to, use $evalAsync (or, honestly, $timeout is close enough; the difference is not that significant).
If you start running into performance issues, you can start looking for cases where you can safely switch to using $digest on specific scopes. (Personally I've not seen many cases where this is worth the added code complexity, but maybe that's just me.)
Related
I was contemplating a means to increase performance in an app while meeting client demands. Right now it seems that the digest runs 5x per second where we only really need it to run perhaps twice per second.
Is there a way to lower the amount of times the digest runs?
In AngularJS there are a set of scenarios when a digest cycle kicks in, some of them are, whenever a value bind to the $Scope or $rootScope object changes(like $scope.text or $rootScope.text), DOM Events (like ng-click, ng-bind etc..), Ajax with callbacks ($http etc..), Timers with callbacks ($timeout, setTimeout etc..), calling $apply, $digest etc..
PFA:
To do:
If you want to reduce the number of digest cycles triggered you'll have to look into each of these above-listed points. Like you can reduce the number of $watchers by using one-time binding (eg: {{::myProperty}} - documentation), limiting the cases of programmatically triggering $apply ($scope.$apply), and replacing $timeouts with $scope.$evalAsync() (because $scope.$evalAsync() will try to trigger in the same digest loop itself whereas $timeout() will wait for the current digest cycle to be done) or un registering the watchers once you found the change and no longer required to watch again like this,
var unregister = $scope.$watch('foo', function () {
// Do something here ...
unregister();
});
etc..
This is probably not the answer you are looking for, however I don't think the digest cycle is the performance killer.
Act upon changes is what may be causing performance issues.
A quick way to avoid the digest cycles acting upon updates is to cache the result of functions instead of binding functions to the HTML templates.
Eg: ng-show="shouldShow()" will be evaluated every time by the digest cycle. If you are able to cache the result of this function in a JS variable in the Controller then use the cached result, you may see performance improvements.
eg: $scope.show = shouldShow(), then ng-show="show"
It seems that one can achieve the same result using two different approaches:
doSomething();
$scope.$digest();
or
$scope.$apply(function() {
doSomething();
});
So what are the differences and what to use when?
AngularJS $digest
Processes all of the watchers of the current scope and its children. Because a watcher's listener can change the model, the $digest() keeps calling the watchers until no more listeners are firing. This means that it is possible to get into an infinite loop. This function will throw 'Maximum iteration limit exceeded.' if the number of iterations exceeds 10.
Usually, you don't call $digest() directly in controllers or in directives. Instead, you should call $apply() (typically from within a directive), which will force a $digest().
If you want to be notified whenever $digest() is called, you can register a watchExpression function with $watch() with no listener.
In unit tests, you may need to call $digest() to simulate the scope life cycle.
Both will do the same thing. But, using $scope.apply() wrapped as a function is best practice. Why because, when you wrap something in $scope.apply(), you can write catch block for that. So, you can catch any exceptions you may face.
You can check if a $digest is already in progress by checking $scope.$$phase.
if(!$scope.$$phase) {
//use anyone yours: $digest or $apply
}
scope.$digest() will fire watchers on the current scope, and on all of its children, too. scope.$apply will evaluate passed function and run $rootScope.$digest().
See this: $apply vs $digest in directive testing
$scope.$digest() : This is cycle process. So when we call $scope.$digest() it will start a digest cycle and check all the watchers of the scope whatever should changed will be changed, and after that a dirty check will be processed , which will check while the digest cycle was is progress is anything changed , if changed then again the digest cycle will start working.One Should not call $scope.$digest() manually because whenever anything change in the scope it will be called . More details here $digest
$scope.$apply: In $scope.$apply you implicitly tell that only watchers affected by the function will be update or only specific watchers will be checked for the update,it handles exception internally , no manual handling needed . More details $apply
I am a little confused about how $scope.$apply and digest loops function. From what I understand, since digest loops runs at regular intervals and not always, we can force the digest loop to run on certain scope variables which we want to update immediately. Also in the description here, it's given that $scope.$apply should be used when an async call is made, so that variables can be updated. My doubt is if digest loop doesn't run always, how are scope variables almost instantaneously updated in the view/controller?
Simply, use $scope.$apply() whenever you're outside the angular scope. For example within setTimeout function, as it is outside the world of angular.
I'm trying to better understand the nuances of using the $timeout service in Angular as a sort of "safe $apply" method. Basically in scenarios where a piece of code could run in response to either an Angular event or a non-angular event such as jQuery or some standard DOM event.
As I understand things:
Wrapping code in $scope.$apply works fine for scenarios where you
aren't already in a digest loop (aka. jQuery event) but will raise an error if a digest is in progress
Wrapping code in a $timeout() call with no delay parameter works whether already in a digest cycle or not
Looking at Angular source code, it looks like $timeout makes a call to $rootScope.$apply().
Why doesn't $timeout() also raise an error if a digest cycle is already in progress?
Is the best practice to use $scope.$apply() when you know for sure that a digest won't already be in progress and $timeout() when needing it to be safe either way?
Is $timeout() really an acceptable "safe apply", or are there gotchas?
Thanks for any insight.
Looking at Angular source code, it looks like $timeout makes a call to
$rootScope.$apply().
Why doesn't $timeout() also raise an error if a digest cycle is already in progress?
$timeout makes use of an undocumented Angular service $browser. Specifically it uses $browser.defer() that defers execution of your function asynchronously via window.setTimeout(fn, delay), which will always run outside of Angular life-cycle. Only once window.setTimeout has fired your function will $timeout call $rootScope.$apply().
Is the best practice to use $scope.$apply() when you know for sure that a digest won't already be in progress and $timeout() when needing it to be safe either way?
I would say so. Another use case is that sometimes you need to access a $scope variable that you know will only be initialized after digest. Simple example would be if you want to set a form's state to dirty inside your controller constructor (for whatever reason). Without $timeout the FormController has not been initialized and published onto $scope, so wrapping $scope.yourform.setDirty() inside $timeout ensures that FormController has been initialized. Sure you can do all this with a directive without $timeout, just giving another use case example.
Is $timeout() really an acceptable "safe apply", or are there gotchas?
It should always be safe, but your go to method should always aim for $apply() in my opinion. The current Angular app I'm working on is fairly large and we've only had to rely on $timeout once instead of $apply().
If we use $apply heavily in the application, we might get the Error: $digest already in progress. It happens because one $digest cycle can be run at a time. We can resolve it by $timeout or by $evalAsync.
The $timeout does not generate error like "$digest already in progress“ because $timeout tells Angular that after the current cycle, there is a timeout waiting and this way it ensures that there will not any collisions between digest cycles and thus output of $timeout will execute on a new $digest cycle.
I tried to explain them at : Comparison of apply, timeout,digest and evalAsync.
May be it will help you.
As far as I understand it, $timeout is a wrapper around setTimeout which implicitly calls $scope.$apply, meaning it runs outside of the angular lifecycle, but kickstarts the angular lifecycle itself. The only "gotcha" I can think of is that if you're expecting your result to be available this $digest, you need to find another way to "safe apply" (which, AFAIK, is only available via $scope.$$phase).
Is there a way to postpone or delay a digest from happening?
I have a bunch of changes that I want to make to a model but I don't want the digest to fire until all changes to the model were made. Some of the objects on the model have watchers that update other objects on the model to change.
Ideally I'd like to
Stop the $digest
Make all changes to the model
Start the $digest
The $digest will find all dirty objects and fire the watchers.
Another solution to this is to, instead of stopping $digest I could
Remove the watchers
Make all changes to the model (digest still runs)
Add the watchers that were removed
After the watchers are added I'd need to run the watcher methods to ensure that the model is in the correct state.
I just feel the 2nd option seems like its a hack.
Ideas??
This is not a hack at all. Its a good question because large data sets can cause the $digest cycle to run very slowly when a user inputs text rapidly or holds down backspace. You can definitely do performance tweaks like being careful with your $watch and $filter functions, but sometimes its a better idea to delay the $digest cycle using a debounce function.