wiki icon indicating copy to clipboard operation
wiki copied to clipboard

避免调用$scope.$apply()时发生$digest already in progress错误

Open huguangju opened this issue 9 years ago • 1 comments

在使用Angular的时候避免不了经常手动更新作用域的情况,特别是在指令中。通常是用 controller 和 directives 的 $apply() 来手动触发 $digest 的。不过直接用它的话,有一定机率会在控制台抛出错误:

Error: $digest already in progress

很多第三方指令中,包括 ui-event 都是这样写来避免报错的:

if (!$scope.$$phase) {
  $scope.$apply();
}

$$phase 是一个标志,是否angular 在 $digest 循环中。如果 $digest 循环中 ,$scope.$$phase 会返回true。

但是官方不建议我们这么做 When-to-use-$scope.$apply():

Do NOT randomly sprinkle it throughout your code. If you are doing if (!$scope.$$phase) $scope.$apply() it's because you are not high enough in the call stack.

最好不要用 $$phase

正确的方式是:

$timeout(function() {
  // anything you want can go here and will safely be run on the next digest.
})

或者

$scope.$evalAsync(function(){
  // ....
}) 

你应该知道的一些细节:

  • $$phase is private to the framework and there are good reasons for that.
  • $timeout(callback) will wait until the current digest cycle (if any) is done, then execute the callback, then run at the end a full $apply.
  • $timeout(callback, delay, false) will do the same (with an optional delay before executing the callback), but will not fire an $apply (third argument) which saves performances if you didn't modify your Angular model ($scope).
  • $scope.$apply(callback) invokes, among other things, $rootScope.$digest, which means it will redigest the root scope of the application and all of its children, even if you're within an isolated scope.
  • $scope.$digest() will simply sync its model to the view, but will not digest its parents scope, which can save a lot of performances when working on an isolated part of your HTML with an isolated scope (from a directive mostly). $digest does not take a callback: you execute the code, then digest.
  • $scope.$evalAsync(callback) has been introduced with angularjs 1.2, and will probably solve most of your troubles.
  • if you get the $digest already in progress error, then your architecture is wrong: either you don't need to redigest your scope, or you should not be in charge of that.

参考:

  1. AngularJS : Prevent error $digest already in progress when calling $scope.$apply()
  2. Why is using if(!$scope.$$phase) $scope.$apply() an anti-pattern?
  3. What is $$phase in AngularJS?

huguangju avatar Nov 05 '15 08:11 huguangju

解决了实际的问题,非常有用,感谢分享!

HiKWang avatar Aug 10 '17 05:08 HiKWang