$ gelten bereits in Bearbeitung Fehler

133

Stapelspur:

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

verweist auf diesen 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
        });
    })

Seltsame Sache, auf meinem LG4X funktioniert es gut, aber auf meinem Samsung S2 wirft es den obigen Fehler. Irgendwelche Ideen, was ist falsch?

Upvote
quelle
1
Haben Sie stackoverflow.com/a/12859093/1266600 ausprobiert ? Dies kann daran liegen, dass unterschiedliche Geräte -> unterschiedliche Verarbeitungsgeschwindigkeiten -> unterschiedliche Timings - an einigen Stellen zu Konflikten führen können, an anderen jedoch nicht.
Sushain97
20
Verwenden Sie$timeout()
Onur Yıldırım
7
+1 auf den Kommentar $ timeout (). Siehe: stackoverflow.com/questions/12729122/…
Trevor

Antworten:

106

Sie erhalten diesen Fehler, weil Sie $applyinnerhalb eines vorhandenen Verdauungszyklus aufrufen .

Die große Frage ist: Warum rufst du an $apply? Sie sollten niemals anrufen müssen, es $applysei denn, Sie haben eine Schnittstelle zu einem Nicht-Angular-Ereignis. Die Existenz von $applybedeutet normalerweise, dass ich etwas falsch mache (es sei denn, das $ apply kommt wieder von einem Nicht-Angular-Ereignis).

Wenn dies hier $applywirklich angemessen ist, sollten Sie einen "Safe Apply" -Ansatz in Betracht ziehen:

https://coderwall.com/p/ngisma

Brian Genisio
quelle
41
Der Kern der verknüpften Safe-Anwendung ist ein Anti-Pattern (gemäß den Dokumenten) github.com/angular/angular.js/wiki/Anti-Patterns . Wenn Sie eine zukünftig unterstützte Methode (die $$ -Phase geht weg!) Möchten, wickeln Sie Ihren Code in ein $ timeout () ohne festgelegte Zeit ein. Es wird sicher angewendet, nachdem der aktuelle Digest-Zyklus abgeschlossen ist.
Betaorbust
@betaorbust Einverstanden. Sichere Anwendung ist schlecht. Wenn Sie zu oft anrufen, kann dies zu Leistungsproblemen führen. Es ist am besten, den Code zu strukturieren, um das Problem insgesamt zu vermeiden.
Brian Genisio
Ich rufe nicht an
Schaltung
41

Sie können diese Anweisung verwenden:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
Dariraze
quelle
1
Es wird nicht empfohlen, Variablen zu verwenden, die mit $$ beginnen, da sie privat sind. In diesem Fall $$ Phase
Ara Yeressian
9
Diese Antwort ist viel hilfreicher als die obige. Ich brauche eine Lösung, um nicht für etwas ermahnt zu werden, das außerhalb meiner Kontrolle liegt. Wir haben eine Mischung aus eckigem und altem Code, und sie müssen irgendwie interagieren. Es ist zu teuer, nur den gesamten alten Code neu zu schreiben ...
Jordan Lapp
24

Wenn in einigen Fällen der Gültigkeitsbereich angewendet werden muss, können Sie eine Zeitüberschreitung festlegen, damit die $ -Anwendung bis zum nächsten Tick verschoben wird

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

oder verpacken Sie Ihren Code in ein $ timeout (function () {..}); weil der Bereich am Ende der Ausführung automatisch $ angewendet wird. Wenn Sie möchten, dass sich Ihre Funktion synchron verhält, würde ich das erste tun.

jeff.d
quelle
Ich fand, dass ich die Aktion in den setTimeout(function() { $apply(function() {... do stuff ...} ) })unten stehenden per @Tamil Vendhan aufnehmen musste.
Prototyp
6
Verwenden Sie nicht setTimeout, da nur ein weiterer $ apply erforderlich ist. Verwenden Sie das Framework, es verfügt über einen $ timeout-Service, der all das für Sie erledigt.
Spencer
10

In meinem Fall verwende ich die $applyeckige Kalender-Benutzeroberfläche, um ein Ereignis zu verknüpfen:

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

Nach dem Lesen des Dokuments des Problems: https://docs.angularjs.org/error/ $ rootScope / inprog

Der Teil Inkonsistente API (Sync / Async) ist sehr interessant:

Stellen Sie sich beispielsweise eine Bibliothek eines Drittanbieters vor, die über eine Methode verfügt, mit der Daten für uns abgerufen werden. Da möglicherweise ein asynchroner Aufruf an einen Server erfolgt, wird eine Rückruffunktion akzeptiert, die beim Eintreffen der Daten aufgerufen wird.

Da der MyController-Konstruktor immer innerhalb eines $ apply-Aufrufs instanziiert wird, versucht unser Handler, einen neuen $ apply-Block innerhalb eines Aufrufs einzugeben.

Ich ändere den Code in:

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

Klappt wunderbar !

Hier haben wir $ timeout verwendet, um die Änderungen am Bereich in einem zukünftigen Aufrufstapel zu planen. Durch die Bereitstellung einer Zeitüberschreitungsperiode von 0 ms wird dies so schnell wie möglich erreicht und $ timeout stellt sicher, dass der Code in einem einzelnen $ apply-Block aufgerufen wird.

mpgn
quelle
1
Ihre $ timeout delay 0-Lösung ist Awesome.
Ahsan
9

Ich denke, in Winkel 1.3 haben sie eine neue Funktion hinzugefügt - $scope.$applyAsync(). Diese Funktionsaufrufe gelten später - sie sagen mindestens 10 ms später. Es ist nicht perfekt, aber es beseitigt zumindest den lästigen Fehler.

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

user3413723
quelle
3

Zu jedem Zeitpunkt kann nur eine $digestoder eine $applyOperation ausgeführt werden. Dies soll verhindern, dass sehr schwer zu erkennende Fehler in Ihre Anwendung gelangen. Mit dem Stack-Trace dieses Fehlers können Sie den Ursprung des aktuell ausgeführten $applyoder verfolgen$digest aufgerufenen Aufrufs , der den Fehler verursacht hat.

Weitere Informationen: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

vergessen
quelle
2

Habe gerade dieses Problem behoben. Es ist hier dokumentiert .

Ich habe $rootScope.$applyzweimal im selben Flow angerufen . Alles, was ich getan habe, ist, den Inhalt der Servicefunktion mit einem zu versehen setTimeout(func, 1).


quelle
1

Ich weiß, es ist eine alte Frage, aber wenn Sie wirklich $ scope brauchen. $ ApplyAsync ();

Akaco
quelle
0

Ich rufe $ scope auf. $ Wendet sich so an, um mehrere Anrufe gleichzeitig zu ignorieren.

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

einfach anrufen

callApply();
Marosdee Uma
quelle