Erreur $ apply déjà en cours

133

Trace de la pile:

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

fait référence à ce 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
        });
    })

Chose étrange, sur mon LG4X, cela fonctionne bien, mais sur mon samsung s2, l'erreur ci-dessus est générée. Des idées qui ne vont pas?

Vote favorable
la source
1
Avez-vous essayé stackoverflow.com/a/12859093/1266600 ? Cela peut être dû au fait que différents périphériques -> différentes vitesses de traitement -> des synchronisations différentes, ce qui peut provoquer des conflits à certains endroits mais pas à d'autres.
sushain97
20
utilisation$timeout()
Onur Yıldırım
7
+1 au commentaire $ timeout (). Voir: stackoverflow.com/questions/12729122/…
Trevor

Réponses:

106

Vous obtenez cette erreur parce que vous appelez $applydans un cycle de digestion existant.

La grande question est: pourquoi appelez-vous $apply? Vous ne devriez jamais avoir besoin d'appeler à $applymoins que vous ne vous connectiez à partir d'un événement non angulaire. L'existence de $applysignifie généralement que je fais quelque chose de mal (à moins que, encore une fois, $ apply ne provienne d'un événement non angulaire).

Si $applycela est vraiment approprié ici, envisagez d'utiliser une approche «d'application sûre»:

https://coderwall.com/p/ngisma

Brian Genisio
la source
41
Le noyau de l'application sécurisée liée est un anti-pattern (selon la documentation) github.com/angular/angular.js/wiki/Anti-Patterns . Si vous voulez une manière future ($$ phase s'en va!) De le faire, enveloppez votre code dans un $ timeout () sans délai. Il s'appliquera en toute sécurité une fois le cycle de digestion actuel terminé.
betaorbust
@betaorbust D'accord. Une application sûre est mauvaise. De plus, appeler apply trop de fois peut entraîner des problèmes de performances. Il est préférable de structurer le code pour éviter le problème dans son ensemble.
Brian Genisio
Je n'appelle pas appliquer
circuits
41

Vous pouvez utiliser cette instruction:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
Dariraze
la source
1
Il n'est pas recommandé d'utiliser des variables commençant par $$ car elles sont privées. Dans ce cas $$ phase
Ara Yeressian
9
Cette réponse est beaucoup plus utile que celle ci-dessus. J'ai besoin d'une solution, pour ne pas être réprimandé pour quelque chose qui pourrait échapper à mon contrôle. Nous avons un mélange de code angulaire et hérité, et ils doivent interagir d'une manière ou d'une autre. C'est trop cher de simplement réécrire tout le code hérité ...
Jordan Lapp
24

Si la portée doit être appliquée dans certains cas, vous pouvez définir un délai d'expiration pour que $ apply soit différé jusqu'au prochain tick

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

ou encapsulez votre code dans un $ timeout (function () {..}); car il appliquera automatiquement la portée à la fin de l'exécution. Si vous avez besoin que votre fonction se comporte de manière synchrone, je ferais la première.

jeff.d
la source
J'ai trouvé que je devais inclure l'action dans le setTimeout(function() { $apply(function() {... do stuff ...} ) })per @Tamil Vendhan ci-dessous.
prototype
6
N'utilisez pas setTimeout, cela crée simplement le besoin d'un autre $ apply. Utilisez le framework, il dispose d'un service $ timeout qui fait tout cela pour vous.
Spencer
10

Dans mon cas, j'utilise $applyavec l'interface utilisateur du calendrier angulaire pour lier un événement:

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

Après avoir lu la doc du problème: https://docs.angularjs.org/error/ $ rootScope / inprog

La partie API incohérente (Sync / Async) est très intéressante:

Par exemple, imaginez une bibliothèque tierce qui a une méthode qui récupérera les données pour nous. Puisqu'il peut effectuer un appel asynchrone à un serveur, il accepte une fonction de rappel, qui sera appelée lorsque les données arriveront.

Puisque le constructeur MyController est toujours instancié à partir d'un appel $ apply, notre gestionnaire essaie d'entrer un nouveau bloc $ apply à partir de celui-ci.

Je change le code en:

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

Fonctionne comme un charme!

Ici, nous avons utilisé $ timeout pour planifier les modifications de la portée dans une future pile d'appels. En fournissant un délai d'expiration de 0 ms, cela se produira dès que possible et $ timeout garantira que le code sera appelé dans un seul bloc $ apply.

mpgn
la source
1
Votre solution $ timeout delay 0 est géniale.
Ahsan
9

Dans angular 1.3, je pense, ils ont ajouté une nouvelle fonction - $scope.$applyAsync(). Ces appels de fonction s'appliquent plus tard - ils disent au moins 10 ms plus tard. Ce n'est pas parfait, mais cela élimine au moins l'erreur ennuyeuse.

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

utilisateur3413723
la source
3

À tout moment, il ne peut y en avoir qu'une seule $digestou une $applyopération en cours. Cela permet d'éviter que des bogues très difficiles à détecter ne pénètrent dans votre application. La trace de pile de cette erreur vous permet de tracer l'origine de l'exécution $applyou de l' $digestappel en cours, qui a provoqué l'erreur.

Plus d'informations: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

oubli
la source
2

Je viens de résoudre ce problème. Son documenté ici .

J'appelais $rootScope.$applydeux fois dans le même flux. Tout ce que j'ai fait, c'est envelopper le contenu de la fonction de service avec un fichier setTimeout(func, 1).


la source
1

Je sais que c'est une vieille question mais si vous avez vraiment besoin d'utiliser $ scope. $ ApplyAsync ();

Akaco
la source
0

J'appelle $ scope. $ S'applique comme ça à un appel ignoré multiple en une fois.

      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);
      }

appelez simplement

callApply();
Marosdee Uma
la source