AngularJS (1.x)
as fast as a lightning
Bartłomiej Narożnik
Agenda
• watch expressions
• ng-repeat
• filters
• binding
• synchronous and asynchronous apply
• To optimize or not to optimize?
Bartłomiej Narożnik - AngularJS performance 2
Digest cycle
Bartłomiej Narożnik - AngularJS performance 3
action
scope.$apply()
Start from
root scope
Check all
watchers for
scope
Check for
more scopes
Update UI
switch to scope
no changes
value changed
all done
• User actions (ng-click, ng-model, etc.)
• $scope.$apply(), $scope.$digest()
• $timeout, $interval
• $q promises resolvance
• $http responses
i.e. a lot of things…
Bartłomiej Narożnik - AngularJS performance 4
When digest runs
action
scope.$apply()
Watch expressions
• {{ }} bindings
• $scope.$watch()
• Most directives (like ng-show)
• filters: {{date | howLongAgo}}
• ng-repeat
• Scope variables scope: {bar: '='}
• And others…
Bartłomiej Narożnik - AngularJS performance 5
Start from
root scope
Check all
watchers for
scope
Check for
more scopes
$watch()
Bartłomiej Narożnik - AngularJS performance 6
Making watch faster
($watchCollection)
Bartłomiej Narożnik - AngularJS performance 7
$scope.$watch('someHugeList', function() {}, true);
$scope.$watchCollection('someHugeList', function() {});
Making watch faster
(Deep watch a subset)
Bartłomiej Narożnik - AngularJS performance 8
$scope.$watch('bookList', function() {}, true);
$scope.$watch(
function() {return _.pluck($scope.bookList, 'id');},
function() {},
true
);
Making watch faster
Bartłomiej Narożnik - AngularJS performance 9
• Make change handler simpler
• Avoid functions in watchers or bindings
• Have less watches 
Make watch faster
(Don’t use watch)
Bartłomiej Narożnik - AngularJS performance 10
// JavaScript (controller)
function updateTitle() {...}
scope.$watch('title', updateTitle);
// HTML
<input type='text' ng-model='title'>
//JavaScript (controller)
$ctrl.saveTitle = function () {...};
// HTML
<input type='text' ng-model='title'
ng-keyup='saveTitle()'>
Unbind watch
Bartłomiej Narożnik - AngularJS performance 11
scope.$watch('title', function(newVal, oldVal){});
var unbinder = scope.$watch('title',
function(newVal, oldVal){});
//this line removes the watch from $$watchers.
unbinder();
Destroy!
Bartłomiej Narożnik - AngularJS performance 12
$scope.$on('$destroy', function () {
// clean up
});
What to clean:
• scope.$watch
• scope.$on
• element.on
• $timeout
• Any other shared variable or reference
ng-repeat
Bartłomiej Narożnik - AngularJS performance 13
ng-repeat optimization
(track by)
<div ng-repeat='word in book'>
scope.book = refreshBook();
<div ng-repeat='word in book
track by word.Key'>
*Introduced in Angular 1.2
Bartłomiej Narożnik - AngularJS performance 14
ng-repeat optimization
(pagination)
<div ng-repeat='word in $ctrl.book'>
<div ng-repeat='word in $ctrl.bookPage'>
Bartłomiej Narożnik - AngularJS performance 15
ng-repeat optimization
(bind-html)
Bartłomiej Narożnik - AngularJS performance 16
<ul>
<li ng-repeat='word in $ctrl.book'>…</li>
</ul>
<ul ng-bind-html='$ctrl.wordList'></ul>
ng-show vs ng-if(ng-switch)
• How frequently will it change?
• How heavy is the scope?
Bartłomiej Narożnik - AngularJS performance 17
Filters
<ul>
<li ng-repeat='word in $ctrl.book | filter:"X"'>…</li>
</ul>
// JavaScript (controller)
$ctrl.filteredWords = $filter('filter')($ctrl.book, 'X');
// HTML
<ul>
<li ng-repeat='word in $ctrl.filteredWords'>…</li>
</ul>
Bartłomiej Narożnik - AngularJS performance 18
One-time binding
<span>{{ ::$ctrl.title }}</span>
<ul>
<li ng-repeat='page in ::$ctrl.pages'></li>
</ul>
*Introduced in Angular 1.3
Bartłomiej Narożnik - AngularJS performance 19
<input type='text' name='searchTerm'
ng-model='search.value'
ng-model-options='{ debounce: 250 }' />
Debounce
Bartłomiej Narożnik - AngularJS performance 20
$digest vs $apply
$scope.bookTitle = 'New Title';
$scope.$digest();
$scope.$apply(function () {
$scope.bookTitle = 'New Title';
});
Bartłomiej Narożnik - AngularJS performance 21
$evalAsync, $applyAsync
$element.on('mouseup', function () {
// do mouseup stuff
$scope.$applyAsync();
});
$element.on('click', function () {
// do click stuff
$scope.$applyAsync();
});
Bartłomiej Narożnik - AngularJS performance 22
The magic line
myApp.config(['$compileProvider',
function ($compileProvider) {
$compileProvider.debugInfoEnabled(false);
}]);
Bartłomiej Narożnik - AngularJS performance 23
Have you tried Angular 2+?
Bartłomiej Narożnik - AngularJS performance 24
When should you optimize?
If your optimization
makes the program harder to understand,
then it's a premature optimization.
Bartłomiej Narożnik - AngularJS performance 25
Bartłomiej Narożnik - AngularJS performance 26
Premature optimization is the root of all evil
Donald Knuth
Coming soon…
Bartłomiej Narożnik - AngularJS performance 27
Thank You!
winkiel@outlook.com
Sources
https://dzone.com/articles/9-ways-to-improve-angularjs-performance
https://www.airpair.com/angularjs/posts/angularjs-performance-large-
applications
https://www.qualtrics.com/eng/tuning-angularjs-performance/
http://www.codelord.net/2014/06/17/angular-performance-101-slides/
https://www.alexkras.com/11-tips-to-improve-angularjs-performance/
https://blog.upstate.agency/improving-angular-performance-with-1-line-of-
code-a1fb814a6476
Bartłomiej Narożnik - AngularJS performance 29

AngularJS (1.x) as fast as a lightning

Editor's Notes

  • #8 watch with three parameters makes deep comparison, watch collection only shallow (on first level)
  • #11 Use callbacks or events instead of $watch function
  • #13 You should always explicitly call your $on('$destroy'), unbind all your watchers and event listeners, and cancel any instances of $timeout, or other asynchronous ongoing interactions. Not doing so will keep them running in the background, wasting your CPU and RAM. Failing to do so will cause memory leaks in older browser, and slow down your Garbage Collector in modern browsers. A very important corollary is that you need to remember to call scope.$destroy before you remove the DOM.
  • #19 Filters are run twice on every digest cycle. First, when the watcher is triggered from changes, and a second time to check any further changes in the digest cycle. Similar to ng-show, filters hide the filtered elements with CSS but do not remove those elements from the DOM.
  • #22 If you are listening to an JavaScript event that happens outside of the Angular ecosystem (jQuery click events for example), you may need to manually trigger a digest cycle to notify other components of your changes. The scope.$apply function triggers at the $rootScope and travels to all child scopes triggering all watchers in the application. Alternatively, the scope.$digest function only triggers a digest cycle on itself and its own child nodes. Thus, you only need to use scope.$apply if a parent scope needs to know about the changes.
  • #23 Instead of triggering a digest for both the mouseup and the click $evalAsync() will execute in the current digest $applyAsync() in a scheduled one