Erro $ Apply já em andamento

133

Rastreio de pilha:

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

refere-se a este código 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
        });
    })

Coisa estranha, no meu LG4X funciona bem, no entanto, no meu samsung s2, lança o erro acima. Alguma idéia que está errado?

Voto a favor
fonte
1
Você já tentou stackoverflow.com/a/12859093/1266600 ? Pode ser porque dispositivos diferentes -> velocidades de processamento diferentes -> tempos diferentes, que podem causar conflitos em alguns lugares, mas não em outros.
sushain97
20
use$timeout()
Onur Yıldırım
7
+1 no comentário $ timeout (). Veja: stackoverflow.com/questions/12729122/…
Trevor

Respostas:

106

Você está recebendo esse erro porque está chamando $applydentro de um ciclo de digestão existente.

A grande questão é: por que você está ligando $apply? Você nunca precisa ligar, a $applymenos que esteja fazendo interface com um evento não angular. A existência de $applygeralmente significa que estou fazendo algo errado (a menos que, novamente, o $ apply ocorra a partir de um evento não angular).

Se $applyrealmente for apropriado aqui, considere usar uma abordagem de "aplicação segura":

https://coderwall.com/p/ngisma

Brian Genisio
fonte
41
O núcleo da aplicação segura vinculada é um antipadrão (de acordo com a documentação) github.com/angular/angular.js/wiki/Anti-Patterns . Se você quiser uma maneira com suporte para o futuro (a fase $$ está indo embora!), Envolva seu código em $ timeout () sem tempo definido. Ele será aplicado com segurança após a conclusão do ciclo de compilação atual.
betaorbust 19/02
@betaorbust Concordou. A aplicação segura é ruim. Além disso, aplicar chamadas muitas vezes pode causar problemas de desempenho. É melhor estruturar o código para evitar o problema todos juntos.
Brian Genisio
im não chamar aplicar
circuito
41

Você pode usar esta declaração:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
Dariraze
fonte
1
Não é recomendável usar variáveis ​​que começam com $$ porque são privadas. Neste caso, fase $$
Ara Yeressian
9
Esta resposta é muito mais útil que a acima. Preciso de uma solução, para não ser advertido por algo que possa estar além do meu controle. Temos uma mistura de código angular e legado, e eles precisam interagir de alguma forma. É muito caro para apenas reescrever todo o código legado ...
Jordan Lapp
24

Se o escopo precisar ser aplicado em alguns casos, você poderá definir um tempo limite para que o $ apply seja adiado até o próximo tick

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

ou envolva seu código em $ timeout (function () {..}); porque ele aplicará automaticamente o escopo no final da execução. Se você precisa que sua função se comporte de forma síncrona, eu faria o primeiro.

jeff.d
fonte
Descobri que precisava incluir a ação dentro do setTimeout(function() { $apply(function() {... do stuff ...} ) })@Tamil Vendhan abaixo.
prototype
6
Não use setTimeout, isso apenas cria a necessidade de outro $ apply. Use a estrutura, ele tem um serviço de timeout $ que faz tudo isso para você.
Spencer #
10

No meu caso, eu uso $applycom a interface do usuário do calendário angular para vincular algum evento:

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

Depois de ler o documento do problema: https://docs.angularjs.org/error/ $ rootScope / inprog

A parte API inconsistente (Sync / Async) é muito interessante:

Por exemplo, imagine uma biblioteca de terceiros que possua um método que recupere dados para nós. Como pode estar fazendo uma chamada assíncrona para um servidor, ele aceita uma função de retorno de chamada, que será chamada quando os dados chegarem.

Como o construtor MyController é sempre instanciado a partir de uma chamada $ apply, nosso manipulador está tentando inserir um novo bloco $ apply a partir de uma.

Eu mudo o código para:

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

Funciona como um encanto !

Aqui usamos $ timeout para agendar as alterações no escopo em uma pilha de chamadas futura. Ao fornecer um período de tempo limite de 0 ms, isso ocorrerá o mais rápido possível e $ timeout garantirá que o código seja chamado em um único bloco $ apply.

mpgn
fonte
1
Sua solução $ timeout delay 0 é impressionante.
Ahsan
9

No angular 1.3, eu acho, eles adicionaram uma nova função - $scope.$applyAsync(). Essas chamadas de função se aplicam mais tarde - elas dizem cerca de 10 ms mais tarde, pelo menos. Não é perfeito, mas pelo menos elimina o erro irritante.

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

user3413723
fonte
3

A qualquer momento, pode haver apenas uma $digestou $applyoperação em andamento. Isso evita que bugs muito difíceis de detectar entrem no seu aplicativo. O rastreamento da pilha desse erro permite rastrear a origem da chamada $applyou execução em execução $digest, que causou o erro.

Mais informações: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

perdoar
fonte
2

Apenas resolvi esse problema. Está documentado aqui .

Eu estava ligando $rootScope.$applyduas vezes no mesmo fluxo. Tudo o que fiz foi agrupar o conteúdo da função de serviço com a setTimeout(func, 1).


fonte
1

Eu sei que é uma pergunta antiga, mas se você realmente precisar usar $ scope. $ ApplyAsync ();

akaco
fonte
0

Eu chamo $ scope. $ Aplicam-se assim a chamadas ignoradas várias de uma vez.

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

simplesmente chame

callApply();
Marosdee Uma
fonte