Angularの「$ digest()の反復回数が10回に達した」エラーのトラブルシューティング方法


91

$ digest()の反復が10回に達しました。中止します!

「最後の5回のイテレーションで発生したウォッチャー:」などの意味で多くのサポートテキストがありますが、このテキストの多くはさまざまな関数からのJavaScriptコードです。この問題を診断するための経験則はありますか?常に軽減できる問題ですか、それともこの問題を単なる警告として扱う必要があるほど複雑なアプリケーションがありますか?

回答:


80

Venが言ったように、$digestサイクルごとに異なる(同一ではない)オブジェクトを返すか、データを何度も変更しています。

アプリのどの部分がこの動作を引き起こしているのかを突き止めるための最速のソリューションは次のとおりです。

  1. すべての不審なHTMLを削除-基本的にすべてのHTMLをテンプレートから削除し、警告がないかどうかを確認します
  2. 警告がない場合-削除したHTMLの小さな部分を追加し、問題が再発するかどうかを確認します
  3. 警告が表示されるまで手順2を繰り返します。HTMLのどの部分が問題の原因であるかがわかります。
  4. さらに調査します-ステップ3の部分は、上のオブジェクトを変更する$scopeか、各$digestサイクルで同一でないオブジェクトを返します。
  5. $digest手順1の後も繰り返し警告が表示される場合は、おそらく非常に疑わしいことをしています。親テンプレート/スコープ/コントローラーに対して同じ手順を繰り返します

また、カスタムフィルターの入力を変更しないようにする必要もあります。

JavaScriptには、通常期待するように動作しない特定のタイプのオブジェクトがあることに注意してください。

new Boolean(true) === new Boolean(true) // false
new Date(0) == new Date(0) // false
new String('a') == new String('a') // false
new Number(1) == new Number(1) // false
[] == [] // false
new Array == new Array // false
({})==({}) // false

5
ありがとうございました!これは役立つヒューリスティックです。AngularのngRepeatの新しい「トラックバイ」機能も私に役立つと思います。私はサービスでアンダースコアを使用していくつかのmap()およびgroupBy()のことをしているので、それは間違いなく毎回「異なる」オブジェクトを返します(それらは論理的に同じものを表します-「Id by track」はAngularがそれらを認識しないのに役立ちます)彼らが本当に持っていない場合は「変更」として)。
ブラスター2013年

2
あなたがやっていてmap()groupBy()あなたのES$watchがダーティチェックを実行することobjectEquality $watch('myFunctionDoingGropby',callback,true) を確認した場合は、docs.angularjs.org
api / ng。$ rootScope.Scope

上記のコメントのとおり、「追跡」によって私の問題が修正されました(ここのドキュメントを参照してください:docs.angularjs.org/api/ng/directive/ngRepeat)。なぜこれが機能するのかを説明するコメントに感謝します...
Soferio

@ g00fy:それは良いリードであり、リスト取得関数を$scope_.map-edリストが一度割り当てられる変数に置き換えることができましたが、一般的に、オブジェクトの等価性によるダーティチェックにどのように影響しますか。$watch作動しているのは手動で作成したものではありませんが、ngRepeat
またはMapper

73

通常、毎回異なるオブジェクトを返すときに発生します。

たとえば、これを次のように使用するとしますng-repeat

$scope.getObj = function () {
  return [{a: 1}, {b: 2}];
};

Angularは「安定性」を保持しようとし、同じ結果が2回返されるまで(と比較して===)関数を実行するため、このエラーメッセージが表示されます。この場合、関数は常にaを返すため、trueは返されません。新しいオブジェクト。

console.log({} === {}); // false. Those are two different objects!

この場合、オブジェクトをスコープに直接格納することで修正できます。例:

$scope.objData = [{a: 1}, {b: 2}];
$scope.getObj = function () {
  return $scope.objData;
};

そうすれば、常に同じオブジェクトを返すことになります!

console.log($scope.objData === $scope.objData); // true (a bit obvious...)

(複雑なアプリケーションであっても、これに遭遇することはありません)。

更新:Angularのウェブサイトにさらに詳しい説明が追加されました


これをどうやって止めますか?私は今、同じような状況にあります。
征服者

2
呼び出しごとに異なるオブジェクトを作成していないことを確認してください;)。
2013年

どうすればこれを確認できますか?func(obj)でitemのようなことをしているのですが、funcが1回ではなく何度も呼び出されているようです。ng-initを使用してfuncを呼び出し、それをスコープのモデルにアタッチしようとしましたが、それも機能しませんでした。
征服者

1
これは、この問題のいたるところで見られる例です。Angularが問題の関数を指すことができれば、これはとても素晴らしいことです。この動作があります...
Jorn

1
@BenWheeler 2013年以降、確かに改善されています。
Ven

11

このソリューションをここに投入したかったのですが、うまくいけば他の人の役に立つでしょう。呼び出されるたびに新しいオブジェクトを作成する生成されたプロパティを繰り返し処理していたため、この反復問題が発生していました。

生成されたオブジェクトが最初に要求されたときにキャッシュし、存在する場合は常にキャッシュを返すことで修正しました。必要に応じてキャッシュされた結果を破棄するdirty()メソッドも追加されました。

私はこのようなものを持っていました:

function MyObj() {
    var myObj = this;
    Object.defineProperty(myObj, "computedProperty" {
        get: function () {
            var retObj = {};

            return retObj;
        }
    });
}

そしてここに実装されたソリューションがあります:

function MyObj() {
    var myObj = this,
        _cached;
    Object.defineProperty(myObj, "computedProperty" {
        get: function () {
            if ( !_cached ) {
                _cached = {};
            }

            return _cached;
        }
    });

    myObj.dirty = function () {
        _cached = null;
    }
}

ああ、神様、私はあなたに10票の賛成票を与えたいと思います。あなたはちょうど私の頭を壁に3時間近くぶつけた問題を解決しました。わたしは、あなたを愛しています!
GMA、

7

また、無限ループにならない可能性もあります。10回の反復は、それを確実に結論付けるのに十分な数ではありません。したがって、野生のガチョウを追いかける前に、まずその可能性を除外することをお勧めします。

そのための最も簡単な方法は、最大ダイジェストループカウントをはるかに大きな数に増やすことです。これは、module.configメソッドを使用してメソッドで実行できます$rootScopeProvider.digestTtl(limit)infdigエラーが発生しなくなった場合は、十分に複雑な更新ロジックがあるだけです。

再帰的な監視に依存するデータまたはビューを構築する場合whileforまたはを使用して、反復解(つまり、新しいダイジェストループの開始に依存しない)を検索することができますArray.forEach。場合によっては、構造が非常に入れ子になっていて再帰的ではないこともあります。そのような場合は、上限を上げる以外にすべきことはあまりありません。

エラーをデバッグする別の方法は、ダイジェストデータを調べることです。JSONをかなり出力すると、配列の配列が得られます。トップレベルの各エントリは反復を表し、各反復はウォッチエントリのリストで構成されます。

たとえば$watch、それ自体で変更されたプロパティがある場合、値が無限に変化していることが簡単にわかります。

$scope.vm.value1 = true;
$scope.$watch("vm.value1", function(newValue)
{
    $scope.vm.value1 = !newValue;
});
[
   [
      {
         "msg":"vm.value1",
         "newVal":true,
         "oldVal":false
      }
   ],
   [
      {
         "msg":"vm.value1",
         "newVal":false,
         "oldVal":true
      }
   ],
   [
      {
         "msg":"vm.value1",
         "newVal":true,
         "oldVal":false
      }
   ],
   [
      {
         "msg":"vm.value1",
         "newVal":false,
         "oldVal":true
      }
   ],
   [
      {
         "msg":"vm.value1",
         "newVal":true,
         "oldVal":false
      }
   ]
]

もちろん、大規模なプロジェクトでは、これはそれほど単純ではない場合があります。特に、時計が補間である場合、msgフィールドには値があることが多い"fn: regularInterceptedExpression"ためです{{ }}

それ以外にも、問題の原因を見つけるためにHTMLを削減するなど、すでに述べた方法はもちろん役に立ちます。


6

私は同じ問題を抱えていました-私は毎回新しい日付を作成していました。だから、日付を扱う人のために、私はすべての呼び出しを次のように変換しました:

var date = new Date(); // typeof returns object

に:

var date = new Date().getTime(); // typeof returns number

日付オブジェクトではなく数値を初期化することで解決しました。


2

簡単な方法は、minファイルではなくangular.jsを使用することです。それを開いて次の行を見つけます:

if ((dirty || asyncQueue.length) && !(ttl--)) {

下に行を追加:

console.log("aaaa",watch)

その後、開発ツールコンソールでページを更新すると、エラーコードが表示されます。



0

また、プロジェクトにあるカスタムディレクティブのtemplateUrlにタイプミスがあると、このエラーメッセージが表示されたことにも触れておきます。タイプミスが原因で、テンプレートをロードできませんでした。

/* @ngInject */
function topNav() {
    var directive = {
        bindToController: true,
        controller: TopNavController,
        controllerAs: 'vm',
        restrict: 'EA',
        scope: {
            'navline': '=',
            'sign': '='
        },
        templateUrl: 'app/shared/layout/top-navTHIS-IS-A-TYPO.html'
    };

Webブラウザーの開発ツールのネットワークタブで、404エラーが発生しているリソースがないかどうかを確認します。

エラーメッセージは非常に不可解であり、実際の問題とは一見無関係であるため、見落としがちです。


0

.otherwise()にルート定義がなく、間違ったルートにアクセスしていたため、プロジェクトでこの問題が発生していました。


0

私がこれをしていたので、私はこの問題を抱えていました

var variableExpense = this.lodash.find(product.variableExpenseList, (ve) => {
               return ve.rawMaterial.id = rawMaterial.id;
});

これの代わりに:(通知= vs ===)、私の単体テストが壊れ始め、私は愚かさを見つけました

var variableExpense = this.lodash.find(product.variableExpenseList, (ve) => {
               return ve.rawMaterial.id === rawMaterial.id;
});

0

動的なツールチップが必要なこの問題に出くわしました…(同じであっても)angularが毎回新しい値として再計算しました。次のように計算値をキャッシュする関数を作成しました。

$ctrl.myObj = {
    Title: 'my title',
    A: 'first part of dynamic toolip',
    B: 'second part of dynamic tooltip',
    C: 'some other value',
    getTooltip: function () {
        // cache the tooltip
        var obj = this;
        var tooltip = '<strong>A: </strong>' + obj.A + '<br><strong>B: </strong>' + obj.B;
        var $tooltip = {
            raw: tooltip,
            trusted: $sce.trustAsHtml(tooltip)
        };
        if (!obj.$tooltip) obj.$tooltip = $tooltip;
        else if (obj.$tooltip.raw !== tooltip) obj.$tooltip = $tooltip;
        return obj.$tooltip;
    }
};

次に、htmlで次のようにアクセスしました。

<input type="text" ng-model="$ctrl.myObj.C" uib-tooltip-html="$ctrl.myObj.getTooltip().trusted">

0

これが私がそれにアプローチして解決策を見つけた方法です:私はテキストをチェックしました、それは示しました:

Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!

ウォッチャーは過去5回の反復で発生しました:[[{"msg": "statement === statment && functionCall()"、 "newVal":[{"id":7287、 "referen ...

だからあなたが見ることができるなら

メッセージ

それはエラーを生成するステートメントです。このメッセージで呼び出された関数を確認したところ、問題のあるものを特定するためだけに、それらすべてから(false)が返されました。それらの1つは、戻り値を変更し続ける関数を呼び出していました。これが問題です。


-3

不思議なことに、私は突然エラーが発生したときにブラウザを再起動するだけでこのエラーを修正しました。

したがって、1つの解決策は、ブラウザのキャッシュをクリアするか、ブラウザを再起動してみることです。

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