Fehlerbehebung bei Angular-Fehlern "10 $ Digest () -Iterationen erreicht"

90

10 $ Digest () Iterationen erreicht. Abbruch!

Es gibt viel unterstützenden Text im Sinne von "Beobachter, die in den letzten 5 Iterationen gefeuert wurden:" usw., aber ein Großteil dieses Textes ist Javascript-Code aus verschiedenen Funktionen. Gibt es Faustregeln für die Diagnose dieses Problems? Ist es ein Problem, das IMMER gemindert werden kann, oder gibt es Anwendungen, die so komplex sind, dass dieses Problem nur als Warnung behandelt werden sollte?

Blaster
quelle

Antworten:

79

Wie Ven sagte, geben Sie entweder in jedem $digestZyklus unterschiedliche (nicht identische) Objekte zurück oder Sie ändern die Daten zu oft.

Die schnellste Lösung, um herauszufinden, welcher Teil Ihrer App dieses Verhalten verursacht, ist:

  1. Entfernen Sie alles verdächtige HTML - entfernen Sie im Grunde Ihr gesamtes HTML aus der Vorlage und prüfen Sie, ob keine Warnungen vorhanden sind
  2. Wenn keine Warnungen angezeigt werden, fügen Sie kleine Teile des von Ihnen entfernten HTML-Codes hinzu und prüfen Sie, ob das Problem erneut auftritt
  3. Wiederholen Sie Schritt 2, bis Sie eine Warnung erhalten. Sie werden herausfinden, welcher Teil Ihres HTML-Codes für das Problem verantwortlich ist
  4. weiter untersuchen - der Teil aus Schritt 3 ist dafür verantwortlich, entweder die Objekte auf dem zu mutieren $scopeoder nicht identische Objekte in jedem $digestZyklus zurückzugeben.
  5. Wenn Sie $digestnach Schritt 1 immer noch Iterationswarnungen haben , tun Sie wahrscheinlich etwas sehr Verdächtiges. Wiederholen Sie die gleichen Schritte für die übergeordnete Vorlage / den übergeordneten Bereich / den übergeordneten Controller

Sie möchten auch sicherstellen, dass Sie die Eingabe Ihrer benutzerdefinierten Filter nicht ändern

Beachten Sie, dass es in JavaScript bestimmte Arten von Objekten gibt, die sich nicht so verhalten, wie Sie es normalerweise erwarten würden:

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
g00fy
quelle
5
Danke dir! Dies ist eine hilfreiche Heuristik. Ich denke auch, dass Angulars neue "Track by" -Funktion für ngRepeat mir auch helfen wird. Ich mache einige map () - und groupBy () -Stücke mit Underscore in einem Dienst, daher werden definitiv jedes Mal "unterschiedliche" Objekte zurückgegeben (obwohl sie logisch dieselben Dinge darstellen - "track by Id" würde Angular helfen, sie nicht zu sehen als "Veränderung", wenn sie es nicht wirklich getan haben).
Blaster
2
Wenn Sie dies tun map()und groupBy()dann sicherstellen, dass Ihre $watches schmutzige Überprüfungen durchführen, objectEquality $watch('myFunctionDoingGropby',callback,true) siehe docs.angularjs.org/api/ng.$rootScope.Scope#$watch
g00fy
Gemäß dem obigen Kommentar hat "Track by" mein Problem behoben (siehe Dokumente hier: docs.angularjs.org/api/ng/directive/ngRepeat ). Ich wäre dankbar für einen erklärenden Kommentar, der erklärt, warum dies funktioniert ...
Soferio
@ g00fy: Das war ein guter Hinweis, und ich konnte meine Listenabruffunktion durch eine Variable auf meiner ersetzen $scope, der die _.map-ed-Liste einmal zugewiesen wird - aber wie würden Sie im Allgemeinen die besagte schmutzige Prüfung durch Objektgleichheit beeinflussen, wenn Es ist keine manuell erstellte $watch, die auslöst, aber ngRepeat?
ODER Mapper
72

Normalerweise passiert dies, wenn Sie jedes Mal ein anderes Objekt zurückgeben.

Wenn Sie dies beispielsweise in a verwenden ng-repeat:

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

Sie erhalten diese Fehlermeldung, weil Angular versucht, die "Stabilität" zu erreichen, und die Funktion ausführt, bis sie zweimal dasselbe Ergebnis zurückgibt (im Vergleich zu ===). In unserem Fall wird dies niemals true zurückgeben, da die Funktion immer a zurückgibt neues Objekt.

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

In diesem Fall können Sie das Problem beheben, indem Sie das Objekt direkt im Gültigkeitsbereich speichern, z

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

Auf diese Weise geben Sie immer das gleiche Objekt zurück!

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

(Das sollten Sie auch bei komplexen Anwendungen niemals antreffen).

Update: Angular hat auf seiner Website ausführlichere Erklärungen hinzugefügt .

Ven
quelle
Wie verhindern Sie dies? Ich habe gerade eine ähnliche Situation.
Eroberer
2
Stellen Sie sicher, dass Sie nicht bei jedem Aufruf unterschiedliche Objekte erstellen;).
Ven
Wie kann ich das sicherstellen? Ich mache so etwas wie item in func (obj), aber func scheint oft aufgerufen zu werden, anstatt einmal, wie ich hoffe. Ich habe versucht, mit ng-init func aufzurufen und es dann an ein Modell im Bereich anzuhängen, aber das hat auch nicht funktioniert.
Eroberer
1
Dies ist das Beispiel, das Sie überall zu diesem Problem sehen. Es wäre so großartig, wenn Angular auf die beleidigende Funktion hinweisen könnte, die dieses Verhalten hat ...
Jorn
1
@ BenWheeler Es hat sich seit 2013 sicherlich verbessert, ja: P.
Ven
11

Ich wollte diese Lösung nur hier reinwerfen, hoffentlich hilft sie anderen. Ich bekam dieses Iterationsproblem, weil ich über eine generierte Eigenschaft iterierte, die bei jedem Aufruf ein neues Objekt erstellte.

Ich habe das Problem behoben, indem ich das generierte Objekt bei der ersten Anforderung zwischengespeichert und dann immer den Cache zurückgegeben habe, falls vorhanden. Es wurde auch eine Dirty () -Methode hinzugefügt, die die zwischengespeicherten Ergebnisse nach Bedarf zerstört.

Ich hatte so etwas:

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

            return retObj;
        }
    });
}

Und hier ist die implementierte Lösung:

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

            return _cached;
        }
    });

    myObj.dirty = function () {
        _cached = null;
    }
}
Asmor
quelle
Oh mein Gott, ich wünschte ich könnte dir 10 positive Stimmen geben. Sie haben gerade ein Problem gelöst, bei dem ich fast 3 Stunden lang meinen Kopf gegen die Wand schlug. Ich liebe dich!
GMA
6

Ich hatte das gleiche Problem - ich habe jedes Mal ein neues Datum erstellt. Für alle, die sich mit Daten befassen, habe ich alle Anrufe wie folgt konvertiert:

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

zu:

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

Das Initialisieren einer Nummer anstelle eines Datumsobjekts hat es für mich gelöst.

Arman Bimatov
quelle
6

Es besteht auch die Möglichkeit, dass es sich überhaupt nicht um eine Endlosschleife handelt. 10 Iterationen sind keine ausreichend große Zahl, um mit Sicherheit darauf schließen zu können. Bevor Sie also einer Wildgansjagd nachgehen, kann es ratsam sein, diese Möglichkeit zuerst auszuschließen.

Die einfachste Methode, dies zu tun, besteht darin, die maximale Anzahl der Digest-Schleifen auf eine viel größere Zahl zu erhöhen, was module.configmit der $rootScopeProvider.digestTtl(limit)Methode in der Methode möglich ist. Wenn der infdigFehler nicht mehr auftritt, haben Sie einfach eine ausreichend komplexe Aktualisierungslogik.

Wenn Sie Daten oder Ansichten bauen auf rekursive Uhren verlassen können Sie sich für iterative Lösungen gesucht werden soll (dh nicht auf neue Digest - Schleifen unter Berufung gestartet werden) verwenden while, foroder Array.forEach. Manchmal ist die Struktur nur stark verschachtelt und nicht einmal rekursiv. In diesen Fällen ist wahrscheinlich nicht viel zu tun, außer das Limit anzuheben.

Eine andere Methode zum Debuggen des Fehlers ist das Betrachten der Digest-Daten. Wenn Sie den JSON hübsch drucken, erhalten Sie ein Array von Arrays. Jeder Eintrag der obersten Ebene stellt eine Iteration dar. Jede Iteration besteht aus einer Liste von Überwachungseinträgen.

Wenn Sie beispielsweise eine Eigenschaft haben, die in a $watchon selbst geändert wird , ist leicht zu erkennen, dass sich der Wert unendlich ändert:

$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
      }
   ]
]

Natürlich ist dies bei größeren Projekten möglicherweise nicht so einfach, zumal das msgFeld häufig den Wert hat, "fn: regularInterceptedExpression"wenn die Uhr eine {{ }}Interpolation ist.

Abgesehen davon sind die bereits erwähnten Methoden, wie das Reduzieren des HTML-Codes, um die Ursache des Problems zu finden, natürlich hilfreich.

HB
quelle
2

Der einfache Weg ist: Verwenden Sie angle.js, nicht die min-Datei. öffne es und finde die Zeile:

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

Zeile unten hinzufügen:

console.log("aaaa",watch)

und aktualisieren Sie dann Ihre Seite. In der Konsole der Entwicklertools finden Sie Ihren Fehlercode.

Askie
quelle
0

Ich möchte auch erwähnen, dass ich diese Fehlermeldung erhalten habe, als ich einen Tippfehler in der templateUrl einer benutzerdefinierten Direktive hatte, die ich in meinem Projekt hatte. Aufgrund des Tippfehlers konnte die Vorlage nicht geladen werden.

/* @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'
    };

Überprüfen Sie auf der Registerkarte "Netzwerk" der Entwicklungstools Ihres Webbrowsers, ob bei einer Ressource ein 404-Fehler vorliegt.

Leicht zu übersehen, da die Fehlermeldung sehr kryptisch ist und scheinbar nichts mit dem eigentlichen Problem zu tun hat.

Casey Plummer
quelle
0

Ich hatte dieses Problem in meinem Projekt, weil in .otherwise () meine Routendefinition fehlte und ich eine falsche Route traf.

Sandeep M.
quelle
0

Ich hatte dieses Problem, weil ich das tat

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

Stattdessen: (note = vs ===) begann mein Unit-Test zu brechen und ich fand meine Dummheit

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

Ich bin auf dieses Problem gestoßen, bei dem ich einen dynamischen Tooltip benötigte ... dies führte dazu, dass Angular ihn jedes Mal als neuen Wert neu berechnete (obwohl er derselbe war). Ich habe eine Funktion erstellt, um den berechneten Wert wie folgt zwischenzuspeichern:

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

Dann habe ich im HTML folgendermaßen darauf zugegriffen:

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

So näherte ich mich dem und fand eine Lösung: Ich überprüfte den Text und zeigte:

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

Beobachter feuerten in den letzten 5 Iterationen: [[{"msg": "Anweisung === statment && functionCall ()", "newVal": [{"id": 7287, "referen ...

Also, wenn Sie das sehen können

Nachricht

Das ist die Aussage, die den Fehler erzeugt. Ich habe die in dieser Nachricht aufgerufene Funktion überprüft und von allen zurückgegeben (false), um festzustellen, welche das Problem haben. Einer von ihnen rief eine Funktion auf, die die Rückgabe ständig ändert, was das Problem ist.

Ahmed M. Matar
quelle
-3

So verrückt es auch klingen mag, ich habe diesen Fehler behoben, indem ich meinen Browser neu gestartet habe, als er plötzlich auftauchte.

Eine Lösung besteht darin, einfach den Cache Ihres Browsers zu leeren oder den Browser neu zu starten.

cst1992
quelle