AngularJS : Prevent error $digest already in progr

2018-12-31 02:30发布

I'm finding that I need to update my page to my scope manually more and more since building an application in angular.

The only way I know of to do this is to call $apply() from the scope of my controllers and directives. The problem with this is that it keeps throwing an error to the console that reads :

Error: $digest already in progress

Does anyone know how to avoid this error or achieve the same thing but in a different way?

25条回答
余欢
2楼-- · 2018-12-31 02:47

This will be solve your problem:

if(!$scope.$$phase) {
  //TODO
}
查看更多
倾城一夜雪
3楼-- · 2018-12-31 02:48

Understanding that the Angular documents call checking the $$phase an anti-pattern, I tried to get $timeout and _.defer to work.

The timeout and deferred methods create a flash of unparsed {{myVar}} content in the dom like a FOUT. For me this was not acceptable. It leaves me without much to be told dogmatically that something is a hack, and not have a suitable alternative.

The only thing that works every time is:

if(scope.$$phase !== '$digest'){ scope.$digest() }.

I don't understand the danger of this method, or why it's described as a hack by people in the comments and the angular team. The command seems precise and easy to read:

"Do the digest unless one is already happening"

In CoffeeScript it's even prettier:

scope.$digest() unless scope.$$phase is '$digest'

What's the issue with this? Is there an alternative that won't create a FOUT? $safeApply looks fine but uses the $$phase inspection method, too.

查看更多
千与千寻千般痛.
4楼-- · 2018-12-31 02:48

You can use

$timeout

to prevent the error.

 $timeout(function () {
                        var scope = angular.element($("#myController")).scope();
                        scope.myMethod();
                        scope.$scope();
                    },1);
查看更多
浮光初槿花落
5楼-- · 2018-12-31 02:49

See http://docs.angularjs.org/error/$rootScope:inprog

The problem arises when you have a call to $apply that is sometimes run asynchronously outside of Angular code (when $apply should be used) and sometimes synchronously inside Angular code (which causes the $digest already in progress error).

This may happen, for example, when you have a library that asynchronously fetches items from a server and caches them. The first time an item is requested, it will be retrieved asynchronously so as not to block code execution. The second time, however, the item is already in cache so it can be retrieved synchronously.

The way to prevent this error is to ensure that the code that calls $apply is run asynchronously. This can be done by running your code inside a call to $timeout with the delay set to 0 (which is the default). However, calling your code inside $timeout removes the necessity to call $apply, because $timeout will trigger another $digest cycle on its own, which will, in turn, do all the necessary updating, etc.

Solution

In short, instead of doing this:

... your controller code...

$http.get('some/url', function(data){
    $scope.$apply(function(){
        $scope.mydate = data.mydata;
    });
});

... more of your controller code...

do this:

... your controller code...

$http.get('some/url', function(data){
    $timeout(function(){
        $scope.mydate = data.mydata;
    });
});

... more of your controller code...

Only call $apply when you know the code running it will always be run outside of Angular code (e.g. your call to $apply will happen inside a callback that is called by code outside of your Angular code).

Unless someone is aware of some impactful disadvantage to using $timeout over $apply, I don't see why you couldn't always use $timeout (with zero delay) instead of $apply, as it will do approximately the same thing.

查看更多
柔情千种
6楼-- · 2018-12-31 02:50

I would advise you to use a custom event rather than triggering a digest cycle.

I've come to find that broadcasting custom events and registering listeners for this events is a good solution for triggering an action you wish to occur whether or not you are in a digest cycle.

By creating a custom event you are also being more efficient with your code because you are only triggering listeners subscribed to said event and NOT triggering all watches bound to the scope as you would if you invoked scope.$apply.

$scope.$on('customEventName', function (optionalCustomEventArguments) {
   //TODO: Respond to event
});


$scope.$broadcast('customEventName', optionalCustomEventArguments);
查看更多
孤独总比滥情好
7楼-- · 2018-12-31 02:51

When you get this error, it basically means that it's already in the process of updating your view. You really shouldn't need to call $apply() within your controller. If your view isn't updating as you would expect, and then you get this error after calling $apply(), it most likely means you're not updating the the model correctly. If you post some specifics, we could figure out the core problem.

查看更多
登录 后发表回答