As a high-level introduction, I would say that you rarely need to initiate your own digest cycle, since angular handles most cases.
This suggests plunging into the question.
As a very high level, the $ digest loop looks like this:
Do: - - - If asyncQueue.length, flush asyncQueue. - - - Trigger all $watch handlers. - - - Check for "too many" $digest iterations. While: ( Dirty data || asyncQueue.length )
So basically $evalAsync adds a function to asyncQueue and asyncQueue digest if necessary. However, if it is already in the digest loop, it will clear asyncQueue , and it will just call the function.
You may notice that this is very similar to safeApply . One difference is that instead of adding a function to asyncQueue it simply calls it, which can happen, for example, in the middle of a loop. Another difference is that it provides and relies on the $$ variable, which must be internal.
The most important difference between $evalAsync and $apply from $digest (I will get $timeout ) below: $evalAsync and $apply starts the digest on $rootScope , but you call $digest in any area. You will need to evaluate your individual case if you think you need this flexibility.
Using $timeout again is very similar to $evalAsync , except that it will always defer it from the current digest cycle, if any.
$timeout(function() { console.log( "$timeout 1" ); }); $scope.$evalAsync(function( $scope ) { console.log( "$evalAsync" ); });
If you are already in the digest cycle give you
$evalAsync $timeout 1
Even though they are called in the reverse order, since the timeout is delayed until the next cycle, the digest that it creates.
EDIT . Questions in the comment. The biggest difference between $apply and $evalAsync , as far as I can tell, is that $apply actually calls the $digest loop. All this means to you that you need to be sure that you call $apply when you are not in the digest loop. For transparency, the following code is also valid:
$scope.$apply(function() { $scope.$evalAsync(function() { }); });
What will call the apply function, add the no-op function to $asyncQueue and start the digest cycle. The docs say it is recommended to use $apply whenever a model changes that might happen in your $evalAsync
The difference between fn(); $scope.apply() fn(); $scope.apply() and $scope.apply(fn) is that $scope.apply(fn) makes a catch attempt for a function and explicitly uses $exceptionHandler . In addition, you can really try to call digest cycles in fn , which will change the way you process your application.
I would also like to point out at this time that it is even more complex in 1.3, assuming that it remains so. There will be a function called $applyAsync , used to defer calls ( link ).
This post has gathered some information from this blog and this one so post plus some of my experiences.
Hope this helps!