Profiling an AngularJS application is crucial for identifying and resolving performance issues, especially as the application grows in size and complexity. The goal is to pinpoint slow components, inefficient digest cycles, memory leaks, and unnecessary watchers that could impact the user experience.
In this guide, we’ll cover several tools and techniques you can use to profile and optimize your AngularJS application.
1. Chrome DevTools (Built-in Tool)
One of the most powerful tools for profiling AngularJS applications is Chrome DevTools. It provides a comprehensive set of features to monitor performance, identify bottlenecks, and analyze memory usage.
Step 1: Open Chrome DevTools
- Open your AngularJS application in Google Chrome.
- Right-click anywhere on the page and select Inspect to open DevTools (or press
Ctrl+Shift+I
). - Go to the Performance tab.
Step 2: Record a Performance Profile
- Click the Record button in the Performance tab.
- Interact with your application (e.g., navigate, input data, etc.) to simulate the behavior you want to profile.
- Click the Stop button once you have captured enough data.
The performance recording will show you various aspects of your application’s performance, including:
- CPU Usage: How much time the browser spends executing JavaScript.
- Frames per Second (FPS): How smoothly the application is rendering the UI.
- Network Activity: Requests made by the app (useful for API calls).
You can zoom in on specific time ranges and identify slow operations or long-running JavaScript functions.
Step 3: Analyze the Flame Graph
After recording, a flame graph is generated. The flame graph shows the call stack and execution time for each function. You can hover over different sections to identify which functions take the longest time and optimize them accordingly.
Step 4: Profile the Memory
You can also use the Memory tab in Chrome DevTools to analyze memory leaks and inspect memory usage over time. Here’s how to do it:
- Go to the Memory tab.
- Choose Heap Snapshot or Allocation Timeline.
- Take a snapshot before and after performing operations in the app.
- Analyze any significant increase in memory usage or the presence of detached DOM nodes that are not being cleaned up, as these could indicate memory leaks.
2. Batarang Extension (AngularJS-specific Debugging Tool)
Batarang is a Chrome extension developed by the AngularJS team specifically for debugging AngularJS applications.
Step 1: Install Batarang
- Go to the Batarang Chrome extension page.
- Click Add to Chrome.
Step 2: Inspect Your Application
Once installed, open the DevTools and click on the Batarang tab. This extension helps you with:
- Profiling $digest Cycles: It shows how many digest cycles are being triggered, and how long they are taking.
- AngularJS-specific Data: It gives you detailed information about AngularJS components such as models, scopes, and controllers.
- Performance Monitoring: Track time spent in digest cycles and detect bottlenecks related to them.
3. ng-profiler (AngularJS Performance Profiler)
ng-profiler is a tool specifically designed for profiling AngularJS applications. It provides detailed insights into performance bottlenecks.
Step 1: Install ng-profiler
- Install ng-profiler by adding it to your AngularJS application:
npm install ng-profiler
Step 2: Enable ng-profiler in Your Application
You can add ng-profiler to your app in the following way:
angular.module('app', ['ngProfiler']);
Once added, ng-profiler will provide detailed logs of:
- The time taken for each digest cycle.
- The total number of watchers.
- Potential performance issues in your AngularJS components.
Step 3: Analyze Profiler Data
After enabling ng-profiler, check the browser’s console for logs related to performance, digest cycles, and watchers. This tool will give you insights into which areas of the app are consuming more time and need optimization.
4. Using console.time
and console.timeEnd
for Manual Profiling
While AngularJS provides built-in tools like ng-profiler
and DevTools, you can also manually profile specific parts of your code with console.time
and console.timeEnd
. This allows you to track the time taken by specific functions.
Example:
angular.module('app')
.controller('MainCtrl', function($scope) {
console.time('someOperation');
$scope.someFunction = function() {
// Simulating a long-running operation
for (let i = 0; i < 1000000; i++) {
// Some intensive operation
}
};
console.timeEnd('someOperation');
});
This will output the time taken for the someOperation
function to run in the browser’s console.
5. Using $rootScope.$on
to Track Digest Cycle Duration
You can manually track the time taken for each $digest
cycle by using $rootScope.$on('$digest')
.
angular.module('app')
.run(function($rootScope) {
$rootScope.$on('$digest', function() {
console.time('digestCycle');
});
$rootScope.$on('$digestEnd', function() {
console.timeEnd('digestCycle');
});
});
This will log the start and end of each digest cycle, helping you pinpoint if your $digest
cycle is taking too long.
6. Profiling Watchers and $digest Cycles
Too many watchers and inefficient $digest
cycles are often the root cause of performance issues in AngularJS applications. Here’s how to profile and debug these:
Step 1: Count Watchers
You can use $$watchersCount
to track the number of watchers:
console.log($scope.$$watchersCount);
If you see that this count is very high, it means that too many watchers are being registered, which can cause slow performance. Aim to reduce the number of watchers by removing unnecessary ones or using one-time bindings (::
).
Step 2: Use $watchCollection
Instead of $watch
If you’re watching large arrays or objects, it’s better to use $watchCollection
instead of $watch
because it’s more efficient in checking changes in arrays and objects.
$scope.$watchCollection('someArray', function(newVal, oldVal) {
// handle changes
});
7. Optimize $digest Cycle and $watchers
If you identify that the $digest
cycle is the source of performance problems, here are a few things you can do to optimize it:
A. Use One-Time Binding (::
)
One-time binding prevents Angular from adding watchers for certain expressions, improving performance.
<span>{{ ::someValue }}</span> <!-- One-time binding -->
B. Debounce User Input
For user input fields that trigger frequent $digest
cycles, you can use debouncing to delay the execution until the user stops typing.
$scope.$watch('userInput', _.debounce(function(newVal) {
// perform expensive operation
}, 300));
C. Minimize Use of $apply
and $timeout
Avoid triggering unnecessary $digest
cycles with $apply()
and $timeout()
unless absolutely necessary. Both of these methods initiate a new digest cycle, which can slow down the app if used excessively.
8. External Tools for Performance Profiling
In addition to Chrome DevTools and AngularJS-specific tools, there are several external tools that can help with performance profiling:
- Lighthouse: Google’s open-source tool for performance audits. It can analyze your AngularJS app and provide actionable insights into performance improvements.
- WebPageTest: A tool that tests the performance of your app in various browsers and network conditions.