As a high level introduction I would say that it is rarely required to actually initiate your own digest cycle, since angular handles most cases.
That being said let's dive 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
is adding the function to the asyncQueue
and defering a digest if it needs to. However if it's already in a digest cycle it will flush the asyncQueue
and it will just call the function.
You may notice that this is very similar to the safeApply
. One difference is that instead of adding the function to the asyncQueue
it just calls it, which can happen in the middle of a cycle for instance. The other difference is that it exposes and relies on a $$
variable, which are intended to be internal.
The most important difference however between $evalAsync
and $apply
from $digest
(I will get to $timeout
below) is that $evalAsync
and $apply
starts the digest at the $rootScope
but you call the $digest
on any scope. You will need to evaluate your individual case if you think you need this flexibility.
Using $timeout
is very similar to $evalAsync
again except that it will always defer it out of the current digest cycle, if there is one.
$timeout(function() {
console.log( "$timeout 1" );
});
$scope.$evalAsync(function( $scope ) {
console.log( "$evalAsync" );
});
If you already in a digest cycle will give you
$evalAsync
$timeout 1
Even though they are called in the opposite order, since the timeout one is delayed to the next digest cycle, which it instantiates.
EDIT For the questions in the comment.
The biggest difference between $apply
and $evalAsync
as far as I can tell is that $apply
is actually triggering the $digest
cycle. To you all this means is that you need to be sure you call $apply
when you aren't in a digest cycle. Just for transparency, the following code is also valid:
$scope.$apply(function() {
$scope.$evalAsync(function() {
});
});
Which will call the apply function, add the no-op function to the $asyncQueue
and begin the digest cycle. The docs say it is recommended to use $apply
whenever the model changes, which could happen in your $evalAsync
The difference between fn(); $scope.apply()
and $scope.apply(fn)
is just that the $scope.apply(fn)
does a try catch for the function and explicitly uses $exceptionHandler
. Additionally, you could actually attempt to cause digest cycles in fn
, which will change how the apply is handled.
I'd also like to point out at this time that it is even more complicated in 1.3 assuming it stays this way. There will be a function called $applyAsync
used to defer apply calls (reference).
This post compiled some information from This blog and this so post plus some of my experience.
Hope this helped!