Błąd $ aplikuj już w toku

134

Ślad stosu:

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

odnosi się do tego kodu 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
        });
    })

Dziwne, na moim LG4X działa dobrze, jednak na moim Samsungu s2 wyrzuca powyższy błąd. Jakieś pomysły, co się stało?

Głosuj za
źródło
1
Czy próbowałeś już stackoverflow.com/a/12859093/1266600 ? Może to być spowodowane różnymi urządzeniami -> różnymi prędkościami przetwarzania -> różnymi czasami, które mogą powodować konflikty w niektórych miejscach, ale nie w innych.
sushain97
20
użycie$timeout()
Onur Yıldırım
7
+1 do komentarza $ timeout (). Zobacz: stackoverflow.com/questions/12729122/…
Trevor

Odpowiedzi:

106

Otrzymujesz ten błąd, ponieważ dzwonisz $applyw ramach istniejącego cyklu trawienia.

Najważniejsze pytanie brzmi: dlaczego dzwonisz $apply? Nigdy nie powinieneś dzwonić, $applychyba że łączysz się z wydarzeniem innym niż Angular. Istnienie $applyzwykle oznacza, że ​​robię coś złego (chyba że, ponownie, $ zastosuj dzieje się ze zdarzenia innego niż Angular).

Jeśli $applynaprawdę jest to właściwe, rozważ zastosowanie podejścia „bezpiecznego stosowania”:

https://coderwall.com/p/ngisma

Brian Genisio
źródło
41
Rdzeniem połączonego bezpiecznego zastosowania jest anty-wzór (zgodnie z dokumentacją) github.com/angular/angular.js/wiki/Anti-Patterns . Jeśli chcesz, aby obsługiwany w przyszłości sposób (faza $$ odchodzi!), Opakuj swój kod w $ timeout () bez ustawionego czasu. Zostanie bezpiecznie zastosowany po zakończeniu bieżącego cyklu podsumowania.
betaorbust
@betaorbust Zgoda. Bezpieczne stosowanie jest złe. Ponadto zbyt częste wywoływanie aplikacji może powodować problemy z perfekcją. Najlepiej jest uporządkować kod, aby razem uniknąć problemu.
Brian Genisio,
nie dzwonię aplikuj
obwody elektryczne
41

Możesz użyć tego oświadczenia:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
Dariraze
źródło
1
Nie zaleca się używania zmiennych zaczynających się od $$, ponieważ są one prywatne. W tym przypadku faza $$
Ara Yeressian
10
Ta odpowiedź jest dużo bardziej pomocna niż powyższa. Potrzebuję rozwiązania, aby nie być upominanym za coś, co może być poza moją kontrolą. Mamy mieszankę kodu kątowego i starszego i muszą one jakoś współdziałać. Przepisanie całego starego kodu jest zbyt drogie ...
Jordan Lapp,
24

Jeśli w niektórych przypadkach trzeba zastosować zakres, możesz ustawić limit czasu, aby $ Apply odłożyć do następnego tiku

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

lub zawiń swój kod w $ timeout (function () {..}); ponieważ automatycznie zastosuje zakres po zakończeniu wykonywania. Jeśli chcesz, aby Twoja funkcja zachowywała się synchronicznie, zrobię to w pierwszej kolejności.

jeff.d
źródło
Uważam, że muszę uwzględnić tę akcję w poniższym setTimeout(function() { $apply(function() {... do stuff ...} ) })per @Tamil Vendhan.
prototyp
6
Nie używaj setTimeout, to po prostu stwarza potrzebę zastosowania kolejnego $ Apply. Użyj frameworka, ma usługę $ timeout, która zrobi to za Ciebie.
Spencer,
10

W moim przypadku używam $apply interfejsu kalendarza kątowego do łączenia niektórych wydarzeń:

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

Po przeczytaniu dokumentacji problemu: https://docs.angularjs.org/error/ $ rootScope / inprog

Część niespójna API (Sync / Async) jest bardzo interesująca:

Na przykład wyobraź sobie bibliotekę innej firmy, która ma metodę, która będzie pobierać dla nas dane. Ponieważ może wykonywać asynchroniczne wywołanie serwera, akceptuje funkcję zwrotną, która zostanie wywołana, gdy nadejdą dane.

Ponieważ konstruktor MyController jest zawsze tworzony z poziomu wywołania $ Apply, nasz program obsługi próbuje wprowadzić nowy blok $ apply z poziomu jednego.

Zmieniam kod na:

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

Działa jak marzenie !

Tutaj użyliśmy $ timeout do zaplanowania zmian zakresu w przyszłym stosie wywołań. Podając okres 0 ms, nastąpi to tak szybko, jak to możliwe, a $ timeout zapewni, że kod zostanie wywołany w pojedynczym bloku $ apply.

mpgn
źródło
1
Twoje rozwiązanie $ timeout delay 0 jest niesamowite.
Ahsan
9

Myślę, że w kątowej 1.3 dodali nową funkcję - $scope.$applyAsync(). Te wywołania funkcji mają zastosowanie później - mówią co najmniej 10 ms później. Nie jest doskonały, ale przynajmniej eliminuje irytujący błąd.

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

user3413723
źródło
3

W dowolnym momencie może być tylko jedna operacja $digestlub $applyw toku. Ma to na celu zapobieżenie bardzo trudnym do wykrycia błędom przed wejściem do aplikacji. Śledzenie stosu tego błędu umożliwia śledzenie pochodzenia aktualnie wykonywanego $applylub $digestwywołania, które spowodowało błąd.

Więcej informacji: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

zapomniałem
źródło
2

Właśnie rozwiązałem ten problem. Jest to udokumentowane tutaj .

Dzwoniłem $rootScope.$applydwa razy w tym samym strumieniu. Wszystko, co zrobiłem, to opakowanie zawartości funkcji usługi rozszerzeniem setTimeout(func, 1).


źródło
1

Wiem, że to stare pytanie, ale jeśli naprawdę potrzebujesz, użyj $ scope. $ ApplyAsync ();

akaco
źródło
0

Wywołuję $ scope. $ Zastosuj w ten sposób, aby zignorować wywołanie wiele razy w jednym czasie.

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

po prostu zadzwoń

callApply();
Marosdee Uma
źródło
0

setTimeoutW takich przypadkach możemy użyć funkcji.

console.log('primary task');

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

}, 0);

Zapewni to, że zadanie dodatkowe zostanie wykonane po zakończeniu wykonywania zadania podstawowego.

Yuvraj Patil
źródło