$ applyは既に進行中です


133

スタックトレース:

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

このコードを参照してください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
        });
    })

奇妙なことに、私のLG4Xでは問題なく動作しますが、私のsamsung s2では上記のエラーがスローされます。どんなアイデアが間違っていますか?


1
stackoverflow.com/a/12859093/1266600を試しましたか?デバイスが異なる->処理速度が異なる->タイミングが異なるために、一部の場所では競合が発生し、他の場所では競合が発生しない可能性があります。
sushain97 2013

20
使用$timeout()
OnurYıldırım2014年

7
$ timeout()コメントに+1します。参照:stackoverflow.com/questions/12729122/...
トレバー

回答:


106

$apply既存の消化サイクル内で呼び出しているため、このエラーが発生しています。

大きな問題は、なぜあなたは電話をしているの$applyですか?$applyAngular以外のイベントからのインターフェースでない限り、呼び出す必要はありません。の存在は$apply通常、私が何か間違ったことをしていることを意味します(ここでも、$ applyが非Angularイベントから発生しない限り)。

$applyここで本当に適切な場合は、「安全な適用」アプローチの使用を検討してください。

https://coderwall.com/p/ngisma


41
リンクされた安全な適用のコアは、アンチドキュメント(ドキュメントによる)github.com/angular/angular.js/wiki/Anti-Patternsです。将来的にサポートされる($$ phaseは廃止されます)方法が必要な場合は、時間を設定せずにコードを$ timeout()でラップします。現在のダイジェストサイクルが完了した後で安全に適用されます。
betaorbust 2014

@betaorbust同意する。安全な適用は悪いです。また、applyを何度も呼び出すと、パフォーマンスの問題が発生することがあります。問題をすべて回避するようにコードを構造化するのが最善です。
Brian Genisio 2014

imはapplyを呼び出さない
回路


41

次のステートメントを使用できます。

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}

1
プライベート変数であるため、$$で始まる変数の使用はお勧めしません。この場合、$$ phase
Ara Yeressian 2015

9
この回答は、上記の回答よりもはるかに役立ちます。解決策が必要です。自分の制御が及ばない可能性があることを警告されないようにしてください。角度のあるコードとレガシーコードが混在しており、それらは何らかの形で相互作用する必要があります。すべてのレガシーコードを単に書き換えるにはコストがかかりすぎます...
Jordan Lapp

24

スコープを適用する必要がある場合は、タイムアウトを設定して、$ applyが次のティックまで延期されるようにすることができます。

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

または、コードを$ timeout(function(){..});でラップします。実行の最後にスコープを自動的に$ applyするからです。関数が同期的に動作する必要がある場合は、最初に行います。


setTimeout(function() { $apply(function() {... do stuff ...} ) })以下の@Tamil Vendhanごとにアクションを含める必要があることがわかりました。
プロトタイプ

6
setTimeoutを使用しないでください。これは、別の$ applyの必要性を作成するだけです。フレームワークを使用してください、それはあなたのためにすべてを行う$ timeoutサービスを持っています。
スペンサー

10

私の場合$apply、角度のカレンダーUIでいくつかのイベントをリンクするために使用します。

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

問題のドキュメントを読んだ後:https : //docs.angularjs.org/error/ $ rootScope / inprog

一貫性のないAPI(同期/非同期)という部分は非常に興味深いものです。

たとえば、データを取得するメソッドを持つサードパーティのライブラリを想像してみてください。サーバーへの非同期呼び出しを行っている可能性があるため、データが到着したときに呼び出されるコールバック関数を受け入れます。

MyControllerコンストラクターは常に$ apply呼び出し内からインスタンス化されるため、ハンドラーは$ applyブロック内から新しい$ applyブロックに入ろうとしています。

コードを次のように変更します。

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

魅力的な作品!

ここでは、$ timeoutを使用して、将来のコールスタックでスコープの変更をスケジュールしました。0msのタイムアウト期間を提供することにより、これはできるだけ早く発生し、$ timeoutは、コードが単一の$ applyブロックで呼び出されることを保証します。


1
$ timeout delay 0ソリューションは素晴らしいです。
アーサン、

9

角度1.3では、彼らは新しい機能を追加したと思います- $scope.$applyAsync()。この関数呼び出しは後で適用されます-彼らは少なくとも約10 ms後で言う。それは完璧ではありませんが、少なくとも迷惑なエラーを排除します。

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


3

どの時点でも、1つしかでき$digestまたは$apply進行中の動作。これは、非常に困難なバグを検出してアプリケーションに入らないようにするためです。このエラーのスタックトレースを使用すると、エラーの原因となった現在実行中$applyまたは$digest呼び出しの発生元をトレースできます。

詳細:https : //docs.angularjs.org/error/$rootScope/inprog?p0=$apply


2

この問題を解決しました。ここに文書化されています

$rootScope.$apply同じ流れで二度電話してた。私がしたことは、サービス関数のコンテンツをでラップすることだけですsetTimeout(func, 1)


1

私はそれが古い質問であることを知っていますが、本当に必要な場合は$ scope。$ applyAsync();を使用してください。


0

このように$ scope。$ applyを呼び出して、一度に複数の呼び出しを無視します。

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

単に電話する

callApply();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.