When does change detection kick in?

Does Angular constantly check for changes in the model data? Considering the fact that the component properties we bind the view to do not inherit from any special class, Angular has no way of knowing which property changed. The only way out for Angular is to constantly query each data-bound property to know its current value and compare it against its old value for changes. Highly inefficient to say the least!

Angular does better than that, as change detection runs get executed only at specific times during app execution. Think carefully about any web application; what causes a view to update?

A view can get updated due to:

  • User input/browser events: We click on a button, enter some text, or scroll the content. Each of these actions can update the view (and the underlying model).
  • Remote XHR requests: This is another common reason for view updates. Getting data from a remote server to show on the grid and getting user data to render a view are examples of this.
  • setTimeout and setInterval timers: As it turns out, we can use setTimeout and setInterval to execute some code asynchronously and at specific intervals. Such code can also update the model. For example, a setInterval timer may check for stock quotes at regular intervals and update the stock price on the UI.

For obvious reasons, Angular change detection too kicks in only when any of these conditions occur.

The interesting part here is not when Angular's change detection kicks in but how Angular is able to intercept all browser events, XHR requests, and setTimeout and setInterval functions.

This feat in Angular is performed by a library called zone.js. As the documentation describes:

"A Zone is an execution context that persists across async tasks."

One of the basic abilities of this library is that it can hook into a piece of code and trigger callbacks when code execution starts and when it ends. The code being monitored could be a sequence of calls that are both synchronous and asynchronous in nature. Consider this example, which highlights the usage:

let zone = new NgZone({ enableLongStackTrace: false });     
let doWork = function () { 
  console.log('Working'); 
}; 
 
zone.onMicrotaskEmpty.subscribe((data:any) => { 
  console.log("Done!"); 
}); 
 
zone.run(() => { 
  doWork(); 
    setTimeout(() => { 
        console.log('Hard'); 
        doWork(); 
    }, 200); 
    doWork(); 
}); 

We wrap a piece of code inside a call to the zone.run call. This code calls the doWork function synchronously twice, interleaved with a setTimeout call that invokes the same function after a lapse of 200 milliseconds.

By wrapping this sequence inside zone.run, we can know when the call execution is complete. In zone terminology, these are turns. The code before zone.run sets up a subscriber that gets called when execution is complete, using the zone.onMicrotaskEmpty function:

If we execute the preceding code, the logs look as follows:

Working  // sync call 
Working  // sync call 
Done!   // main execution complete  
Hard     // timeout callback 
Working  // async call 
Done!   // async execution complete

The onMicrotaskEmpty subscription is executed twice, once after the sequential execution completes (defined inside run callback) and one after the asynchronous setTimeout execution is complete.

Angular change detection uses the same technique to execute our code within zones. This code could be an event handler, which internally makes more synchronous and asynchronous calls before completing, or it could be a setTimeout/setInterval operation that may again require a UI update.

The Angular change detection framework subscribes to the onMicrotaskEmpty observable for the executing zone, and kicks in change detection whenever a turn is complete. The following diagram highlights what happens when code similar to the one just described is run on a button click:

During the execution of the code block, if the zone library determines that the call is asynchronous in nature, it spawns a new micro task that has its own life cycle. It is the completion of these micro tasks that also triggers onMicrotaskEmpty.

If you want to know how the change detection trigger looks inside Angular, here is an excerpt from the Angular source code (simplified further):

class ApplicationRef { 
 
  constructor(private zone: NgZone) { 
    this._zone.onMicrotaskEmpty.subscribe(
{next: () => { this._zone.run(() => { this.tick(); }); }}); } tick() {
this._views.forEach((view) => view.detectChanges()); } }

The ApplicationRef class tracks all the change detectors attached throughout the app and triggers a change detection cycle when the application-level zone object fires the onMicrotaskEmpty event. We will shortly touch upon what happens during this change detection.

Zone.js gets the ability to track execution context across any asynchronous call because it overrides the default browser API. The override, also termed monkey patching, overrides the event subscription, XHR requests, and setTimeout/setInterval API. In the example highlighted previously, the setTimeout we invoke is a monkey-patched version of the original browser API.

Now that we know how change detectors are set up and when this activity kicks in, we can look at how it works.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.3.208