What is $evalAsync()
?
$evalAsync()
is an AngularJS method used to defer scope updates by scheduling a function to run before the next digest cycle. It helps in preventing “digest already in progress” errors and improving performance by batching updates efficiently.
Why Use $evalAsync()
?
Avoids “$digest already in progress” errors when updating the scope within an ongoing digest cycle.
Improves performance by deferring execution until Angular is ready.
Ensures changes are applied in the next digest cycle without triggering an immediate $digest()
.
1️⃣ The Problem: Digest Cycle Errors
If you try modifying the scope inside an asynchronous function or a directive without wrapping it in $apply()
, Angular won’t detect the changes.
Without $evalAsync()
(Error)
app.controller('MainCtrl', function($scope) {
setTimeout(function() {
$scope.count = 1; // Angular doesn’t know about this update
}, 1000);
});
Problem: The change to $scope.count
won’t be reflected in the UI because Angular is unaware of the update.
2️⃣ Solution: Using $apply()
(But Can Cause Digest Errors)
A common way to solve this is by using $apply()
to trigger a digest.
Using $apply()
app.controller('MainCtrl', function($scope) {
setTimeout(function() {
$scope.$apply(function() { // Forces a digest cycle
$scope.count = 1;
});
}, 1000);
});
This works, but calling $apply()
inside an ongoing digest cycle may cause errors.
3️⃣ Using $evalAsync()
to Avoid Digest Errors
Instead of forcing a digest, $evalAsync()
schedules an update before the next digest cycle.
With $evalAsync()
(No Digest Errors)
app.controller('MainCtrl', function($scope) {
setTimeout(function() {
$scope.$evalAsync(function() { // Schedules update before next digest
$scope.count = 1;
});
}, 1000);
});
Now, Angular updates the UI safely without triggering errors.
4️⃣ Key Differences: $apply()
, $applyAsync()
, $evalAsync()
Feature | $apply() | $applyAsync() | $evalAsync() |
---|---|---|---|
Triggers digest immediately? | Yes | No | No |
Defers execution? | No | Yes (end of execution cycle) | Yes (before next digest) |
Handles digest errors? | No | Yes | Yes |
Use Case | Immediate updates | Batching multiple updates | Updating within directives, avoiding errors |
5️⃣ Using $evalAsync()
in Directives
When building custom directives, using $evalAsync()
ensures that updates happen without extra digest cycles.
✅ Example: Custom Directive with $evalAsync()
app.directive('customDirective', function() {
return {
restrict: 'E',
template: '<div>{{ message }}</div>',
link: function(scope, element, attrs) {
element.on('click', function() {
scope.$evalAsync(function() {
scope.message = 'Button Clicked!';
});
});
}
};
});
Ensures the scope update occurs safely without forcing a digest.
6️⃣ Using $evalAsync()
for Repeated Updates
If you’re handling multiple async updates, $evalAsync()
batches them efficiently.
Example: Processing a Large Data Set
app.controller('MainCtrl', function($scope) {
$scope.items = [];
for (let i = 0; i < 100; i++) {
setTimeout(function() {
$scope.$evalAsync(function() {
$scope.items.push('Item ' + i);
});
}, i * 10);
}
});
Prevents excessive digest cycles while efficiently updating $scope.items
.
7️⃣ When to Use $evalAsync()
Inside custom directives to avoid extra digest cycles.
When handling asynchronous operations (setTimeout, AJAX).
When making incremental updates to a large dataset.
When modifying scope within an ongoing digest cycle.
Summary
Scenario | Use $apply() | Use $applyAsync() | Use $evalAsync() |
---|---|---|---|
Immediate UI updates needed | Yes | No | No |
Avoiding “digest already in progress” errors | No | Yes | Yes |
Handling frequent async updates | No | Yes | Yes |
Inside custom directives | No | No | Yes |