AngularJS : $apply vs $timeout vs $digest vs $evalAsync

AngularJS – an awesome framework with lots of native services and functions. Some of them are $apply, $timeout, $digest and $evalAsync. These are pretty used terms in any of the AngularJS apps, but sometimes these are a bit confusing with actual differences between them. So I decided to dig them down a bit further. Let’s see what I found for them.

Angular JS
Angular JS

1) $apply :

AngullarJS allow any value to be used as a binding target. Then at the end of any JavaScript code run, check to see if the value has changed. The big benefit is that we can use normal objects and update our data however we want, and the changes will be noticed and reflected in our bindings and now $apply come into the picture.

$apply() is the function which is responsible to execute the entire list of watchers of all available scope in the application by invoking $digest on rootScope ($rootScopr.$digest()). This means that every time we call $apply() we execute a new life complete cycle of the application.

As per AngularJS documentation:

  1. The expression is executed using the $eval() method.
  2. Any exceptions from the execution of the expression are forwarded to the $exceptionHandler service.
  3. The watch listeners are fired immediately after the expression was executed using the $digest() method.

In actual scenarios, it is rare when we need to invoke $apply manually because all native AngularJS services automatically wrap in it. It is only needed when we use any 3rd party library because Angular does not aware about changes made by any 3rd party library, so we explicitly wrap our code in $apply.

In the below example, the first code will not update the binded UI control with “message” while second will do that because “setTimeout” is not natively part of AngularJS.

function MyCtrl($scope) {
    $scope.message = "Hi";
      setTimeout(function () {
          $scope.message = "Hello!";
          // AngularJS unaware of update to $scope
      }, 2000);
}

function MyCtrl($scope) {
    $scope.message = "Hello";
      setTimeout(function () {
          $scope.$apply(function () {
              $scope.message = "Hi";
          });
      }, 2000);
}

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.

2) $digest :

AngularJS executes in cycles, which is termed as “$digest” and this “$digest” is responsible for evaluating changes between models and views and update UI and Model to be in sync.

As already described above that $apply invokes $digest on rootScope and its means that it reevaluate all watchers in all available scope and this may cause a performance hit for large scope hierarchy.

Sometimes we only want to re-evaluate specific scope and its children and this can easily achieve by invoking $digest() on desired scope. It is important to know that the parent scope is not being updated with the new information, hence the changes won’t be reflected if the expression is also used by the parent scope but for any child scope it will reflect.

3) $timeout :

$timeout() was the easiest available solution to resolve problems related to invoking code outside the Angular environment prior to AngularJS 1.2.X.

Basically $timeout service is used to call another JavaScript function after a given time delay. The $timeout service only schedules a single call to the function. The $timeout() process is the regular Javascript setTimeout function which by default, at the end of it, triggers $apply(). You can also instruct $timeout() not to invoke the $apply() function.

 

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.

4)$evalAsync :

$evalAsync() is a new function which was first introduced in AngularJS 1.2.X, and in my opinion it is the adroit version of $timeout().

Before $evalAsync() came into the picture, AngularJS team recommended using the $timeout in case of digest cycle issue, described above.

As AngularJS evolved for large apps and digest cycle became common and then Angular team introduced $evalAsync(). This function will evaluate the expression during the current cycle or the next.

Ok, let me explain a little bit more about it.

  • Suppose you call some expression on $evalAsync and any digest cycle is going on at the same time. All expressions from $evalAsync will be added to the queue and they will be part of the current lifecycle and no new digest cycle will be invoked. This is why $evalAsync make it more efficient because it reduce the possibility to create new digest every time.
  • If there is not any digest is going on, it will work similar to $timeout. It will create a callback with default time (10 ms) and after this time (10 ms), there will be a new digest on rootScope and all expressions will be evaluated.