Delaying the $digest() cycle in AngularJS

When scaling an AngularJS app, large data sets can cause the $digest() cycle to run slowly. There are a number of performance optimizations you can make, such as being careful with $watch() and $filter(), using one-time binding, or hunting down expensive operations with Batarang. But sometimes, even with these improvements, an app with a lot of data can feel sluggish due to the $digest() cycle. A classic example is keyboard input. If you have a lot of data and your $digest() cycle is getting kicked off every time a user enters a character into an input or textarea, this causes the processor to go into overdrive when someone is typing rapidly or holding down the backspace button. And then you see significant delays in the text updating on the screen.

The best thing that I’ve found for dealing with this issue is to delay the $digest() cycle using a debounce function. What’s a debounce function, you ask? Well, John Hann wrote a great explanation and implementation using pure JS way back in the late 2000’s. Years later, people ported this pattern to custom angular services. These were more “angularish”, but added a little clutter to your code. But now, Angular 1.3 has officially added support for debounce, so delaying the digest cycle is super easy to do.

TL;DR A debounce function is a function that, as long as it continues to be invoked, will not be triggered. It will only be called after it stops being called for X milliseconds.

For example, you can use a debounce function of 150ms on a text input to prevent the $digest() cycle from being triggered until someone stops entering text for 150 milliseconds. And with Angular’s new native support, it is easier than ever. All you need is a single HTML attribute and you can delay the $digest() cycle like this:

<input ng-model="" ng-model-options="{ debounce: 150 }" />

So what is the best debounce delay in milliseconds? Well, it depends on how heavy your $digest() cycle is. On the one hand, the shorter your debounce, the shorter the delay after someone starts typing to when the $digest() fires off and completes. This will determine the perceived responsiveness of the app. Too long, and your app will no longer seem snappy. But on the other hand, if your debounce period is too short, the $digest() cycle will get kicked off too often, which will cause an even greater perceived slowness in your application. You’ll have to figure out what works best for your application.

Happy optimizing!