Understanding AngularJS Digest Cycle

Let’s take a peek at how Angular works underneath the hood. How do we get this magical data binding to work in only a few lines of code? We know by now that whenever a change is made to the model, it gets applied to the view and when ever a change is made to the view, it gets applied to the model. So, how does AngularJS achieve this? A newbie might think that AngularJS achieves this using some kind of polling mechanism where it it keeps checking the model at an interval and makes the updates but, AngularJS handles this in a more sophisticated way. Lets try to understand that in more detail.

The idea behind AngularJS model-changes tracking mechanism is based on the observation that at the end of the day, there are only a finite(and rather small) number of situations where model changes can occur.

Those include:

  1. DOM events(user changing the value of an input field, clicking on a button to invoke JS, and so on.)
  2. XHR responses firing callbacks
  3. Browser’s location changes
  4. Timers (setTimeout, setInterval) firing the callbacks

In AngularJS terminology, the process of detecting model changes is called $digest cycle/loop. The name comes from the function $digest that is available on scope instances.

There are two major components of the $digest loop:

  1. The $watch list
  2. The $evalAsync list

angular-js-digest-cycle

The $watch list

Every time we track an event in the view, we are registering a callback function that we expect to
run when an event occurs on the page. Lets understand this using an example:

In the code above, every time a user updates the input field, {{ firstname }} changes in the UI. This change happens because we bound the input field in the UI to the $scope.name property. In order to update the view, Angular needs to track the change. It does so by adding a watch function to the $watch list. Properties that are on the $scope object are only bound if they are used in the view. In the case above, we’ve added a single function to the $watch list. These $watch lists are resolved in the $digest loop through a process called dirty checking.

Dirty Checking

Dirty checking is a process where in it checks whether a value has changed that hasn’t yet been synchronized across the app. Angular keeps track of the values of the current watches and walks down the $watch list. If the updated value has not changed from the old value, it continues down the list. If the value has changed, the app records the new value and continues down the $watch list. Once Angular has run through the entire $watch list, if any value changed, the app will fall back into the $watch loop until it detects that nothing has changed.

The $watch method on the $scope object sets up a dirty check on every call to $digest inside the Angular event loop. The Angular $digest loop always returns if it detects changes on the expression.

$watchCollection

AngularJS also allows us to set shallow watches for object properties or elements of an array and fire the listener callback whenever the properties change. Using $watchCollection allows us to detect when there are changes on an object or array, which gives us the ability to determine when items are added, removed, or moved in the object or array. $watchCollection works just like the normal $watch works in the $digest loop, and we can treat it as a normal $watch.

$evalAsync List

The $evalAsync() method is a way to schedule running expressions on the current scope at some time in the future. The second operation that the $digest loop runs is executing the $$asyncQueue. We can get access to this work queue with the $evalAsync() method. Throughout the $digest loop, the queue empties between each loop through the dirty checking lifecycle, which means two things for any function called with $evalAsync:

  1. The function will execute sometime after the function that called it.
  2. At least one $digest cycle will be performed after the expression is executed.

Top