$apply already in progress error
Asked Answered
P

12

143

Stack trace:

Error: $apply already in progress
at Error (<anonymous>)
at beginPhase (file:///android_asset/www/built.min.js:7:22740)
at Object.Scope.$apply (file:///android_asset/www/built.min.js:7:25967)
at navigator.geolocation.getCurrentPosition.that (file:///android_asset/www/built.min.js:13:8670)
at Object.geolocation.getCurrentPosition (file:///android_asset/www/plugins/org.apache.cordova.core.geolocation/www/geolocation.js:122:13)
at Object.getCurrentPosition (file:///android_asset/www/built.min.js:13:8589)
at Object.getCurrentPosition (file:///android_asset/www/built.min.js:13:8277)
at Object.getCurrentCity (file:///android_asset/www/built.min.js:13:8941)
at Object.$scope.locateDevice (file:///android_asset/www/built.min.js:13:10480)
at file:///android_asset/www/built.min.js:7:12292:7

refers to this code http://pastebin.com/B9V6yvFu

    getCurrentPosition: cordovaReady(function (onSuccess, onError, options) {

        navigator.geolocation.getCurrentPosition(function () {
            var that = this,
                args = arguments;

            if (onSuccess) {
                $rootScope.$apply(function () {
                    onSuccess.apply(that, args);
                });
            }
        }, function () {
            var that = this,
                args = arguments;
            if (onError) {
                $rootScope.$apply(function () {
                    onError.apply(that, args);
                });
            }
        }, {
            enableHighAccuracy: true,
            timeout: 20000,
            maximumAge: 18000000
        });
    })

Strange thing, on my LG4X it works fine, however on my samsung s2 it throws the above error. Any ideas whats wrong?

Porphyroid answered 5/9, 2013 at 0:46 Comment(3)
Have you tried https://mcmap.net/q/53562/-angularjs-prevent-error-digest-already-in-progress-when-calling-scope-apply? It might be because different devices -> different processing speeds -> different timings, which may cause conflicts in some places but not others.Zelma
use $timeout()Bardo
+1 to the $timeout() comment. See: #12729622Musk
H
108

You are getting this error because you are calling $apply inside an existing digestion cycle.

The big question is: why are you calling $apply? You shouldn't ever need to call $apply unless you are interfacing from a non-Angular event. The existence of $apply usually means I am doing something wrong (unless, again, the $apply happens from a non-Angular event).

If $apply really is appropriate here, consider using a "safe apply" approach:

https://coderwall.com/p/ngisma

Hautevienne answered 5/9, 2013 at 0:52 Comment(3)
The core of the linked safe apply is an anti-pattern (according to the docs) github.com/angular/angular.js/wiki/Anti-Patterns. If you want a future-supported ($$phase is going away!) way of doing it, wrap your code in a $timeout() with no time set. It will safely apply after the current digest cycle has completed.Photodynamics
@Photodynamics Agreed. Safe apply is bad. Also, calling apply too many times can cause perf issues. It is best to structure the code to avoid the issue all together.Hautevienne
im not calling applyNuminous
W
58

Just use $evalAsync instead of $apply.

Wreath answered 23/10, 2014 at 12:39 Comment(1)
Good article explaining evalAsync at panda-os.com/2015/01/angularjs-apply-digest-and-evalasync/…Costermonger
S
44

You can use this statement:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
Sac answered 5/9, 2013 at 10:51 Comment(2)
It is not recommended to use variables which start with $$ because they are private. In this case $$phaseAttend
This answer is much more helpful than the one above. I need a solution, not to be admonished for something that might be beyond my control. We have a mixture of angular and legacy code, and they have to interact somehow. It's too expensive to just rewrite all the legacy code...Waneta
B
24

If scope must be applied in some cases, then you can set a timeout so that the $apply is deferred until the next tick

setTimeout(function(){ scope.$apply(); });

or wrap your code in a $timeout(function(){ .. }); because it will automatically $apply the scope at the end of execution. If you need your function to behave synchronously, I'd do the first.

Bed answered 5/9, 2013 at 2:33 Comment(2)
I found i needed to include the action within the setTimeout(function() { $apply(function() {... do stuff ...} ) }) per @Tamil Vendhan below.Staceestacey
Don't use setTimeout, that just creates the need for another $apply. Use the framework, it has a $timeout service that does all that for you.Miele
C
10

In my case i use $apply with angular calendar UI to link some event:

$scope.eventClick = function(event){           
    $scope.$apply( function() {
        $location.path('/event/' + event.id);
    });
};

After reading the doc of the problem: https://docs.angularjs.org/error/$rootScope/inprog

The part Inconsistent API (Sync/Async) is very interesting:

For example, imagine a 3rd party library that has a method which will retrieve data for us. Since it may be making an asynchronous call to a server, it accepts a callback function, which will be called when the data arrives.

Since, the MyController constructor is always instantiated from within an $apply call, our handler is trying to enter a new $apply block from within one.

I change the code to :

$scope.eventClick = function(event){           
    $timeout(function() {
        $location.path('/event/' + event.id);
    }, 0);
};

Works like a charm !

Here we have used $timeout to schedule the changes to the scope in a future call stack. By providing a timeout period of 0ms, this will occur as soon as possible and $timeout will ensure that the code will be called in a single $apply block.

Chacma answered 1/2, 2016 at 11:7 Comment(1)
Your $timeout delay 0 solution is Awesome.Recce
S
9

In angular 1.3, I think, they added a new function - $scope.$applyAsync(). This function calls apply later on - they say about 10 ms later at least. It is not perfect, but it does at least eliminate the annoying error.

https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$applyAsync

Steinbok answered 5/11, 2015 at 1:52 Comment(0)
C
3

At any point in time, there can be only one $digest or $apply operation in progress. This is to prevent very hard to detect bugs from entering your application. The stack trace of this error allows you to trace the origin of the currently executing $apply or $digest call, which caused the error.

More info: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

Conceive answered 8/10, 2014 at 3:55 Comment(0)
D
2

Just resolved this issue. Its documented here.

I was calling $rootScope.$apply twice in the same flow. All I did is wrapped the content of the service function with a setTimeout(func, 1).

Dorise answered 30/10, 2013 at 19:26 Comment(0)
N
2

I know it's old question but if you really need use $scope.$applyAsync();

Nerti answered 1/7, 2017 at 5:22 Comment(0)
H
0

I call $scope.$apply like this to ignored call multiple in one times.

      var callApplyTimeout = null;
      function callApply(callback) {
          if (!callback) callback = function () { };
          if (callApplyTimeout) $timeout.cancel(callApplyTimeout);

          callApplyTimeout = $timeout(function () {
              callback();
              $scope.$apply();
              var d = new Date();
              var m = d.getMilliseconds();
              console.log('$scope.$apply(); call ' + d.toString() + ' ' + m);
          }, 300);
      }

simply call

callApply();
Hartnett answered 6/10, 2016 at 10:9 Comment(0)
C
0

We can use setTimeout function in such cases.

console.log('primary task');

setTimeout(function() {
  
  console.log('secondary task');

}, 0);

This will make sure that secondary task will be executed when execution of primary task is finished.

Celestecelestia answered 9/11, 2020 at 12:15 Comment(0)
D
0

You can $apply your changes only if $apply is not already in progress. You can update your code as

if(!$scope.$$phase) $scope.$apply();
Dormeuse answered 7/1, 2021 at 2:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.