Writing Robust AngularJS Web Applications
Entering the $digest loop less frequently
Usually the $digest loop is triggered from within AngularJS built-in directives
and services, and we don't need to control it manually. Still, we should be aware of
circumstances under which the $digest loop gets triggered. In general there are four
types of events that are listened to by AngularJS directives and services, and result in
the call to scope.$apply() method:
- Navigation events: Users clicking on links, using back and forward buttons,
and so on. - Network events: All the calls to the $http service (and resources created
with $resource) will trigger a $digest loop when a response (either success
or error) is ready. - DOM events: All the AngularJS directives corresponding to DOM events
(ng-click, ng-mouseover, and so on) will trigger a $digest loop when an
event handler is firing. - JavaScript timers: The $timeout service wraps the JavaScript setTimeout
function and will trigger a $digest loop when a timer fires.
As you can see the $digest loop can run and evaluate all the watches quite often,
especially where there are many DOM event handlers starting the loop. There are
situations where we can't do much about it, but there are certain techniques we can
use to minimize the frequency at which AngularJS enters the $digest loop.
Firstly we can try to minimize the number of network calls by arranging back-end's
API in a way that an individual user action results in one single XHR call and one
single response. Of course this is not always possible, but if you've got full control
over your back-end, this might be a technique to consider. Not only would it limit
number of network calls, but would also mean that AngularJS enters the $digest
loop less frequently.
Next, we must pay special attention to the usage of timers, especially ones wrapped
in the $timeout service. By default the $timer service will call scope.$apply
each time a timer fires, and this can have farfetched consequences if we are not
careful enough. To see what could go wrong, let's consider a simple clock directive
displaying current time:
.directive('clock', function ($timeout, dateFilter) {
return {
restrict: 'E',
link: function (scope, element, attrs) {
function update() {
// get current time, format it and update DOM text