Angular supports so-called data-bindings. That is, the ability to connect fragments of the DOM to Javascript variables. Once a binding has been imposed, it is possible for a modification to a variable to have an impact on the DOM without any intervention on the part of the developer.

Angular certainly isn't the only framework to support this concept: the particularity lies in the way in which Angular achieves this result. Let's suppose we want to generate a simple timer:

The modification of the value of a scope variable is propagated on the view only after Angular has been notified of the desire to apply it with a call to $scope.$apply() . To avoid filling the application code with these kinds of calls, Angular makes a comprehensive library of objects the only scope of which is to call $scope.$apply() for us at the correct time. In our case, we can make use of the $interval service:

Imposing a service as $interval from above in this way is questionable in and of itself, but the fundamental problem is the following: Angular, not knowing precisely which and how many variables have been changed when the final phase of rendering carried out, is forced to perform a dirty-check of every binding activity in the page, to identify any values that differ from those previously used in the last rendering. If such changes are present, the associated change-listeners are triggered, which may themselves cause changes to the scope! In this fashion, the process repeats itself until an absence of changes has been detected.

You don't need a masters in computer science to realize that this results in an extremely costly and non-optimized operation. A single minor modification to the scope can trigger many hundred or thousands of checks within the application. It is for this reason that the Angular community has always stated that it is good practice to limit the total number of bindings present in an application to 2000. If this threshold is breached, good performance can no longer be guaranteed.

To avoid any misunderstanding, it's important to bear in mind that this set-up was a conscious choice made by the Angular team, who were trying to find an "acceptable" trade-off between performance and ease of writing for developers. The problem is that it's very easy to go over this theoretical limit in applications of average complexity, and up until version 1.3 of Angular, there weren't any official alternatives in place to work around the problem.