![]()
The error $apply already in progress in AngularJS occurs when you attempt to manually trigger a $digest cycle using $scope.$apply() while AngularJS is already in the middle of running a $digest cycle. AngularJS runs its $digest cycle automatically when certain actions (like model updates, events, or external asynchronous functions) trigger the view to be updated. If you manually call $apply() while AngularJS is already processing the $digest cycle, it will throw this error to prevent an infinite loop or conflicting changes.
Step 1: Understanding $apply and $digest in AngularJS
In AngularJS, $digest is the process where the framework checks all watched variables (scope variables bound to the view) to see if their values have changed. When a change is detected, Angular updates the view to reflect the model’s new state.
$scope.$apply(): This method is used to trigger a$digestcycle manually when changes happen outside of Angular’s normal context (for example, changes made in third-party libraries or asynchronous events like HTTP requests).$digestcycle: Angular automatically runs this cycle when the view needs to be updated, either after user interactions, timeouts, or events.
If you call $scope.$apply() inside the middle of a $digest cycle, AngularJS will throw the $apply already in progress error to prevent the cycle from triggering multiple times.
Step 2: Causes of $apply already in progress
Here are the common reasons why you might encounter this error:
- Calling
$apply()Inside Another$digestCycle: When AngularJS is already running its$digestcycle, triggering$apply()will cause AngularJS to enter another$digestcycle within the ongoing one, which leads to this error. - Calling
$apply()InsidesetTimeoutorsetInterval: If you use$scope.$apply()inside asetTimeout()orsetInterval()function, and these are triggered during the$digestcycle, Angular will detect that$apply()is being invoked while a$digestcycle is already running. - Using
$apply()Inside an Asynchronous Callback: If you invoke$apply()inside an asynchronous callback (like in an HTTP request or a callback from a third-party library) that gets executed while the$digestcycle is ongoing, the error will occur. - Nested
$apply()Calls: If$apply()is called inside a function that’s itself inside a$scope.$apply()or$timeout(), this will lead to nested$apply()calls, causing the error.
Step 3: How to Fix the $apply already in progress Error
1. Avoid Calling $apply() Inside Another $apply() or $digest Cycle
Ensure you don’t call $apply() from within another $apply() or $digest cycle. For example, if you are calling $apply() manually after an event or timeout, make sure it’s not happening during an already-running cycle.
// Incorrect: Calling $apply inside another $apply
$scope.$apply(function() {
$scope.$apply(function() {
$scope.someVar = 'value'; // This will throw $apply already in progress error
});
});
2. Use $evalAsync() for Asynchronous Updates
If you need to update the model asynchronously but don’t want to trigger an additional $digest cycle, use $scope.$evalAsync(). This method schedules an expression to be evaluated in the next $digest cycle, rather than immediately triggering a new cycle.
$scope.someFunction = function() {
$scope.$evalAsync(function() {
// Update the model asynchronously without triggering an additional $digest cycle
$scope.someVar = 'new value';
});
};
Using $evalAsync() ensures that the update is handled in the next $digest cycle rather than causing nested cycles.
3. Use $timeout() Instead of $apply()
Instead of manually calling $apply(), you can use $timeout() to delay the execution of the code and allow Angular to run the $digest cycle before running the code. $timeout() automatically triggers the $digest cycle and handles this for you.
$timeout(function() {
$scope.someVar = 'new value'; // $timeout ensures digest cycle handling
}, 0);
This ensures that Angular will handle the update properly without conflicting with the existing $digest cycle.
4. Check for Nested $timeout() Calls
Ensure that $timeout() isn’t being used in a nested manner, which could cause the $apply already in progress error.
$timeout(function() {
$scope.someVar = 'new value';
$timeout(function() {
// This could cause a nested $apply problem
$scope.someVar2 = 'another value';
}, 0);
}, 0);
5. Make Sure $apply() Is Not Called in Event Listeners
If $apply() is called inside an event listener (such as $scope.$on()), and the event is triggered while the $digest cycle is already running, you’ll get the $apply already in progress error.
$scope.$on('someEvent', function(event, data) {
$scope.$apply(function() {
// This will cause $apply already in progress error if event is triggered inside an ongoing digest cycle
$scope.someVar = data;
});
});
To fix this, use $evalAsync() or $timeout() instead of $apply().
6. Use $rootScope for Global Changes
For global changes to the model, instead of using $apply() directly on a local scope, consider using $rootScope to trigger changes across the application.
$rootScope.$apply(function() {
// Update model globally
$scope.someVar = 'new value';
});
This helps avoid conflicts between nested $apply() calls on individual controllers or scopes.
Step 4: Debugging the Issue
- Log the Execution Flow: Add
console.log()statements in your$apply()and$digestcode to ensure that it is not being triggered unexpectedly or in an overlapping context.
console.log('Before $apply');
$scope.$apply(function() {
console.log('Inside $apply');
$scope.someVar = 'new value';
});
console.log('After $apply');
This will help you identify where the $apply() call is being made and how the cycle is running.
- Track
$digestCycle Triggers: Check whether the$digestcycle is running multiple times and whether any asynchronous code is involved in the triggering. You can use$scope.$$phaseto check if a$digestcycle is already in progress.
if (!$scope.$$phase) {
$scope.$apply(function() {
$scope.someVar = 'new value';
});
}
This ensures that $apply() is only called if the $digest cycle is not already in progress.
Step 5: Final Considerations
- Avoid Manual
$apply()in Most Cases: AngularJS’s automatic handling of$digestcycles works in most situations. Use$apply()sparingly and prefer$timeout(),$evalAsync(), or$scope.$watch()when appropriate. - Async Code Handling: When using third-party libraries, AJAX requests, or
setTimeout, make sure that the model changes are handled asynchronously to avoid disrupting AngularJS’s digest cycle. - Event Handling: When updating scope variables in response to events or callbacks, make sure the
$digestcycle is triggered correctly without causing conflicts by using$evalAsync().
