Angular performance : watch your watches

Camille Promérat 08/06/2016 r&D

Introduction

Angular does a lot of thing for you. Do you remember your first AngularJS presentation ? There was magic when Igor clicks on a checkbox and the boolean value changed accordingly. This first effect on the random jquery developper absolutey explains the success of this framework.

<script type= »text/javascript » src= »//www.google.fr/trends/embed.js?hl=fr&q=AngularJS&cmpt=q&tz=Etc/GMT-2&tz=Etc/GMT-2&content=1&cid=TIMESERIES_GRAPH_0&export=5&w=500&h=330″></script>

Every magic comes with a price. Angular has this notion of watcher. Every scope maintains a $$watchers, a list of functions responsible to synchronize the DOM element with the value of the angular scope variable. Since the value of a variable can change, Angular has a life-cycle waiting for stability to display the DOM. Every time you mustache a scope variable in a HTML template, angular creates a watcher. You can also add a watcher in javascript in order to execute arbitrary code. It is obvious that the more watchers you have, the more time is required for the digest cycle to run. Consequently, it is very important to measure the number of watchers you have in your angular application.

Tool

The first step is then to install a chrome extension to monitor the number of watchers. This extension is : Angular watchers. It allows you to visualize the number of watchers that your application is consuming. It requires the debug mode to be activated. Once you have do that you should get the view in your developer mode.

angularThe angular watcher extension running on angular documentation page.

Good numbers

There is no « good » number. The lesser the better. I have seen but your angular application can manage several thousand of watchers. The most important is to avoid watcher leaks… So, like Martin Fowler says, it is better to track the trend than the number.

Custom watchers vs Mustashing

One developer recently asked me why it is so bad to write $scope.$watch whereas it is ok to have databinding in the templates. It is equaly bad. The difference is in the performance penalty you can introduce with custom watcher. The only limit is your creativity to implement very slow code . I think this is also what motivates the people to think that it is bad to databind scope functions.

Tracking down a watcher leak after each scroll

I had a leak. On a large tabular view, everytime I scroll, I had +500 watchers.

I removed the leak with this fix:

–      this.container.removeChild(badNodes[i]);

+      $(badNodes[i]).remove();

As you can see, I replaced a native access to the DOM with a JQuery one. JQuery actually traverse the DOM and calls the $destroy function on all childs. In the end, the directive had a handler on the destroy event :

link: function (scope, element) {

  element.on(‘$destroy’, function () {

    scope.$destroy();

  });

}

The destruction of the scope freed the associated watchers. After this modification, I still had +500 watchers (more data loaded by the scoll) followed by -500 watchers (the destruction of elements).

Conclusion

Regarding watcher leaks, it is important to apply the boy scout rule : « Leave the campground cleaner than you found it« . Note that changing the route will modify the ng-view and angular cleans everything. It is the same with ng-if for exemple. So, you have pay attention to the watchers leak for long user interactions on the same view.

N'hésitez pas à partager cette page !
Share on Facebook
Facebook
Share on Google+
Google+
Tweet about this on Twitter
Twitter
Share on LinkedIn
Linkedin
Pin on Pinterest
Pinterest