Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2011 (Minneapolis, MN) with: Haley Groves
Ben Nadel at cf.Objective() 2011 (Minneapolis, MN) with: Haley Groves

Scope.$applyAsync() vs. Scope.$evalAsync() in AngularJS 1.3

By Ben Nadel on

A while back, I looked at using the Scope.$evalAsync() method as a means to execute code asynchronously within an AngularJS context without using the $timeout() service. In AngularJS 1.3, they've added a new Scope method - Scope.$applyAsync(). After reading the documentation, the difference between new $applyAsync() method and the existing $evalAsync() method was not readily apparent. As such, I wanted to dig through the AngularJS source code to see how these two asynchronous execution methods differ.


 
 
 

 
Scope.$applyAsync() vs. Scope.$evalAsync() in AngularJS 1.3.  
 
 
 

Both $applyAsync() and $evalAsync() will update a global queue of pending expressions (each method deals with its own global queue).

Both $applyAsync() and $evalAsync() evaluate an expression asynchronously; however, only $evalAsync() can accept a collection of override "locals" to be used during the evaluation.

Both $applyAsync() and $evalAsync() will schedule a timeout as a backup to make sure that the expression gets executed in a timely manner; however, $evalAsync() will only initialize a timer if the control flow is not already inside a $digest. $applyAsync(), on the other hand, will always schedule a timeout; that said, $applyAsync() will cancel the timer if it can evaluate the expression before the timer is needed.

Both $applyAsync() and $evalAsync() execute the pending expression inside a try/catch block and then pipe errors to the $exceptionHandler() service.

So far, the two methods appear to do exactly the same thing. The difference doesn't become evident until you look at the actual $digest execution. When AngularJS executes a digest, it walks the Scope tree and executes $watch() bindings until no more dirty data is produced. During this lifecycle, both the $applyAsync() queue and the $evalAsync() queue get flushed; but, this happens in two very different places.

The $applyAsync() queue only gets flushed at the top of the $digest before AngularJS starts checking for dirty data. As such, the $applyAsync() queue will be flushed, at most, one time during a $digest and will only get flushed if the queue was already populated before the $digest started.

NOTE: Within the $digest, $applyAsync() will only flush if the current scope is the $rootScope. This means that if you call $digest on a child scope, it will not implicitly flush the $applyAsync() queue.

The $evalAsync() queue, on the other hand, is flushed at the top of the while-loop that implements the "dirty check" inside the $digest. This means that any expression added to the $evalAsync() queue during a digest will be executed at a later point within the same digest.

To make this difference more concrete, it means that asynchronous expressions added by $evalAsync() from within a $watch() binding will execute in the same digest. Asynchronous expressions added by $applyAsync() from within a $watch() binding will execute at a later point in time (~10ms).

Outside of a $digest, both the $applyAsync() method and the $evalAsync() method will populate their respective queues and then initialize a timer. When the timer executes, it triggers a $digest on the $rootScope.

Now, even with this level of understanding, it's still not entirely clear what the need for the new $applyAsync() method is. I suppose it's somewhat more likely to allow for DOM (Document Object Model) rendering before the expression is evaluated; but, I don't believe this is guaranteed (depending on when $watch() bindings were initialized).

To get more answers, I looked in the AngularJS source code to see if the $applyAsync() method was being used internally. And it was - within the $http service. It looks like the $httpProvider now allows you to flag the use of $applyAsync() so that AJAX (Asynchronous JavaScript and JSON) requests that complete within a ~10ms timeframe can be grouped into the same $digest (rather than triggering an individual $digest per AJAX response).

So, this looks like a micro-optimization that allows you to group sets of asynchronous expressions. I'll have to noodle on this a bit more; even after all this, I'm still not sure I would know when or why to replace $evalAsync() method calls with $applyAsync() method calls.



Looking For A New Job?

Ooops, there are no jobs. Post one now for only $29 and own this real estate!

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

I found it useful to rename $evalAsync. I call it $digestAsync

Now, if we compare $digestAsync vs. $applyAsync it is quite obvious what are the differences:

1. $applyAsync queues a request for $apply while $digestAsync queues a request for $digest

2. Executing $applyAsync inside apply phase will not cause additional apply while executing $digestAsync inside of a digest cycle will not cause additional digest cycle

Which one to use ?

I guess the answer is the same as asking "should I use $apply or $digest?"

Nice post, can you tell me how you get the nice Angular outputs in your console, is this some add-on or you did it yourself?

Another key service that uses $evalAsync is $q, I've been tracing through its functionality.

Can you tell me anything whether there is any difference between how the DOM gets repainted when using $apply / $applyAsync / $evalAsync?

I'm trying to help out on an indexedDB library and we're finding that making a series of async calls which invoke $rootScope.$apply is repainting the DOM on each call. I've found that since $q.defer().resolve(...) implicitly invokes $evalAsync and therefore $digest, I'm wondering if we should avoid directly calling $apply and just rely on letting $q take care of handling the digest for us... Any thoughts?

https://github.com/bramski/angular-indexedDB/issues/19

@Danny,

All good questions. All of the DOM manipulation, in AngularJS, is done through directives like ngIf, ngRepeat, ngShow, ngHide, etc. Each of these handles DOM manipulation for their particular use-case; so, there's not one-size-fits-all explanation. That said, the directives do seem to follow the general implementation pattern:

function link( scope, element ) {
. . . scope.$watch( someAttribute, function() { ... do DOM manipulation ... } );
}

Typically, the directive does DOM manipulation in response to a $watch() callback. This is why it is generally *safer* to query the DOM in $evalAsync() if that DOM is being manipulated by another directive -- it gives that other directive "time" to mutate things during its $watch() callbacks.

That said, AngularJS is also very tightly integrated with asynchronous things like $timeout() and $q(). If you are using those, you do NOT NEED to explicitly call $apply() since those services will automatically call it for you. As such, if you call it directly, you're only making the framework do more work.

Without more details, that's really all I can say.

@Marco,

The output is just from the "console" object in Firefox's Firebug plugin. But, Chrome devtools has very similar features:

console.log( "some data" )
console.info( "some data" );
console.error( "an errror" );
console.warn( "a warning" );
console.table( "an array of hashes" );

There's even more functions than that - I think the console object can do "timing" and what not, but I've never really used it.

@Ori,

I would probably just default to using $evalAsync() unless I had a reason to using $applyAsync(). Since I've written this, I don't think that I've actually come across a use case for it that really made a lot of sense in my head. But, I'll keep thinking.

@Ben,

Thanks for your reply. I was more keen on understanding rendering, rather than manipulation; as in, how repaints and re-renders get triggered after the $digest process and whether there is a difference in how things get rendered if using $apply versus $applyAsync versus $evalAsync. It sounded from the article like $applyAsync has about a 10ms threshold for subsequent async requests to get 'grouped' together in the same $digest: "requests that complete within a ~10ms timeframe can be grouped into the same $digest (rather than triggering an individual $digest per AJAX response)." So, I was curious whether you knew if $evalAsync worked similarly and whether this means that angular will only trigger 1 DOM re-render for these grouped updates. Sounds like $apply would trigger a re-render every time it is called.

I'm currently investigating some performance issues on a angular indexedDB service here: https://github.com/bramski/angular-indexedDB/issues/19

We've noticed that the UI freezes when we use an upsert method over a large data set (the example was upserting 1000+ items into an indexed DB). Mr. Spock would say that the UI freezing when making a database transaction is highly illogical. I'm not sure how familiar you are with indexedDB, but it was written to work through async transactions; so when upserting an item into the database, in angularland you would ideally use the $q deferred/promise approach and handle the results of the transaction in a .then().catch() chain. Or with the built in .onsuccess .onerror listeners that come stock in vanilla JS. The problem is that the original author of the library created an arbitrary wrapper around $q, then wrapped every single one of these database transaction methods with $rootScope.$apply and used his strange $q wrapper concoction to resolve the request. We believe the use of $apply is a dealbreaker because it's triggering a re-render for each of the upserts that get applied to the database. Not to mention pointless since $q handles the apply for us already.

(phew)

Anyway, that's the situation. After doing some digging, we found that since $q contains a call to $evalAsync internally, calling $apply explicitly is (exactly like you said) causing the framework to do MUCH more work. I guess one of the takeaways for people is to wield $apply, $applyAsync, and $evalAsync carefully, especially when using the $http or $q services, since both of those do that internally already. But that's broaching a completely different topic which I'm sure you've already covered :]

Thanks again!

(and sorry about the novel)

@Stepan,

no , only $applyAsync() execute $rootscope.$digest(). $digestAsync() ,though there is no such thing , runs digest cycle for its current scope and its children scopes , which is not similar to $rootscope.$digest().

I ran into interesting situation:

1. $evalAsync in primary component adds new item to the queue.
2. It is however cleared in the $digest run by another component on its scope (imagine both catch document.addEventListener('click', ...).
3. So the scope of the primary component is not updated (cf. if (asyncQueue.length) condition in $evalAsync).

So effectively another component stole $digest command to my component which remains unupdated.

1. executes evalasync of expression in current scope while current scope digest is in progress( scope.$digest() is in progress not $rootScope.digest() )
2. So applyasync queue will not be flushed
3. but at every digest loop evalasync queue is going to be flushed, that means there is no $rootScope.$digest() happens at this time.
4. will it contradict with the statement

"Outside of a $digest, both the $applyAsync() method and the $evalAsync() method will populate their respective queues and then initialize a timer. When the timer executes, it triggers a $digest on the $rootScope."

5. that means sometime evalasync triggers $rootScope.$digest() by triggering timeout and sometime not triggers $rootScope.$digest() (as evalasync is already executed in the digest of individual scope(scope$digest() ) while it is in progress)