Come risolvere l'errore angolare "10 $ digest () iterazioni raggiunte"


91

10 $ digest () iterazioni raggiunte. Aborting!

C'è molto testo di supporto nel senso di "Watchers licenziato nelle ultime 5 iterazioni:", ecc., Ma molto di questo testo è codice Javascript di varie funzioni. Esistono regole pratiche per diagnosticare questo problema? È un problema che può SEMPRE essere mitigato, o ci sono applicazioni abbastanza complesse da dover essere trattato come un semplice avvertimento?

Risposte:


80

come ha detto Ven, o stai restituendo oggetti diversi (non identici) ad ogni $digestciclo, oppure stai alterando i dati troppe volte.

La soluzione più veloce per capire quale parte della tua app sta causando questo comportamento è:

  1. rimuovi tutto l'HTML sospetto: in pratica rimuovi tutto il tuo HTML dal modello e controlla se non ci sono avvisi
  2. se non ci sono avvisi, aggiungi piccole parti dell'html rimosso e controlla se il problema si è ripresentato
  3. ripeti il ​​passaggio 2 finché non ricevi un avviso: scoprirai quale parte del tuo html è responsabile del problema
  4. indagare ulteriormente: la parte del passaggio 3 è responsabile della modifica degli oggetti su $scopeo della restituzione di oggetti non identici su ciascuno$digest ciclo.
  5. se hai ancora $digestavvisi di iterazione dopo il passaggio 1, probabilmente stai facendo qualcosa di molto sospetto. Ripetere gli stessi passaggi per modello / ambito / controller padre

Vuoi anche assicurarti di non alterare l'input dei tuoi filtri personalizzati

Tieni presente che in JavaScript ci sono tipi specifici di oggetti che non si comportano come ti aspetteresti normalmente:

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
Grazie! Questa è una utile euristica. Penso anche che la nuova funzione "track by" di Angular per ngRepeat possa aiutare anche me. Sto facendo alcune cose map () e groupBy () usando Underscore in un servizio, quindi sta sicuramente restituendo oggetti "diversi" ogni volta (anche se logicamente rappresentano le stesse cose - "track by Id" aiuterebbe Angular a non vederli come un "cambiamento" se non lo hanno fatto davvero).
blaster

2
Se lo stai facendo map()e poi groupBy()assicurati che il tuo $watches esegua il controllo sporco, objectEquality $watch('myFunctionDoingGropby',callback,true) vedi docs.angularjs.org/api/ng.$rootScope.Scope#$watch
g00fy

Come da commento sopra, "track by" ha risolto il mio problema (vedere i documenti qui: docs.angularjs.org/api/ng/directive/ngRepeat ). Sarei grato per un commento esplicativo che spiega perché funziona ...
Soferio

@ g00fy: è stato un buon vantaggio, e sono stato in grado di sostituire la mia funzione di recupero dell'elenco con una variabile sulla mia $scopeche viene assegnata _.mapuna volta alla lista -ed - ma nel caso generale, come influenzeresti detto controllo sporco dall'uguaglianza degli oggetti se non è uno creato manualmente $watchche sta inciampando, ma ngRepeat?
OPPURE Mapper

73

Di solito ciò accade quando restituisci ogni volta un oggetto diverso.

Ad esempio, se lo usi in un ng-repeat:

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

Riceverai questo messaggio di errore perché Angular cerca di avere la "stabilità" ed eseguirà la funzione finché non restituisce lo stesso risultato 2 volte (rispetto a ===), che nel nostro caso non restituirà mai vero perché la funzione restituisce sempre un nuovo oggetto.

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

In questo caso, puoi risolverlo memorizzando direttamente l'oggetto nello scope, ad es

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

In questo modo restituisci sempre lo stesso oggetto!

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

(Non dovresti mai incontrarlo, anche su applicazioni complesse).

Aggiornamento: Angular ha aggiunto alcune spiegazioni più approfondite sul proprio sito web .


Come impedisci che ciò accada? Sto vivendo una situazione simile in questo momento.
Conqueror

2
Assicurati di non creare oggetti diversi a ogni chiamata;).
Ven

Come posso assicurarmelo? Sto facendo qualcosa come item in func (obj), ma func sembra essere chiamato molte volte invece di una volta come spero. Ho provato a usare ng-init per chiamare func e quindi collegarlo a un modello sull'oscilloscopio, ma nemmeno questo ha funzionato.
Conqueror

1
Questo è l'esempio che vedi ovunque su questo problema. Sarebbe fantastico se Angular potesse indicare la funzione incriminata, che ha questo comportamento ...
Jorn

1
@ BenWheeler È sicuramente migliorato dal 2013, sì: P.
Ven

11

Volevo solo lanciare questa soluzione qui, si spera che possa aiutare gli altri. Stavo riscontrando questo problema di iterazione perché stavo iterando su una proprietà generata che creava un nuovo oggetto ogni volta che veniva chiamato.

L'ho risolto memorizzando nella cache l'oggetto generato la prima volta che è stato richiesto e quindi restituendo sempre la cache se esisteva. È stato anche aggiunto un metodo dirty (), che distruggerebbe i risultati memorizzati nella cache se necessario.

Ho avuto qualcosa di simile:

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

            return retObj;
        }
    });
}

Ed ecco con la soluzione implementata:

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

            return _cached;
        }
    });

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

Oh mio Dio, vorrei poterti dare 10 voti positivi. Hai appena risolto un problema che mi ha fatto sbattere la testa contro il muro per quasi 3 ore. Ti amo!
GMA

7

C'è anche la possibilità che non sia affatto un loop infinito. 10 iterazioni non è un numero sufficientemente elevato per concludere con certezza. Quindi, prima di andare a caccia di oche selvatiche, potrebbe essere consigliabile escludere prima questa possibilità.

Il metodo più semplice per farlo è aumentare il numero massimo di cicli digest a un numero molto maggiore, cosa che può essere eseguita nel module.configmetodo, utilizzando il $rootScopeProvider.digestTtl(limit)metodo. Se lainfdig errore non viene più visualizzato, è sufficiente disporre di una logica di aggiornamento sufficientemente complessa.

Se crei dati o viste che si basano su orologi ricorsivi, potresti voler cercare soluzioni iterative (cioè non fare affidamento su nuovi cicli digest da avviare) usando while, foro Array.forEach. A volte la struttura è solo altamente annidata e nemmeno ricorsiva, probabilmente non c'è molto da fare in quei casi se non aumentare il limite.

Un altro metodo per eseguire il debug dell'errore è esaminare i dati del digest. Se stampi abbastanza il JSON, ottieni un array di array. Ogni voce di primo livello rappresenta un'iterazione, ogni iterazione consiste in un elenco di voci di controllo.

Se ad esempio hai una proprietà che si modifica in a $watchsu se stessa è facile vedere che il valore cambia all'infinito:

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

Ovviamente in un progetto più ampio questo potrebbe non essere così semplice, soprattutto perché il msgcampo ha spesso il valore "fn: regularInterceptedExpression"se l'orologio è un{{ }} un'interpolazione.

Oltre a questo, i metodi già menzionati, come tagliare l'HTML per trovare l'origine del problema, sono ovviamente utili.


6

Ho avuto lo stesso problema: creavo una nuova data ogni volta. Quindi per chiunque abbia a che fare con le date ho convertito tutte le chiamate in questo modo:

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

per:

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

L'inizializzazione di un numero invece di un oggetto data ha risolto il problema.


2

il modo più semplice è: usa angular.js, non il file min. aprilo e trova la riga:

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

aggiungi la riga sotto:

console.log("aaaa",watch)

e poi aggiorna la tua pagina, nella console degli strumenti di sviluppo, troverai il codice di errore.



0

Vorrei anche menzionare che ho ricevuto questo messaggio di errore quando ho avuto un errore di battitura nel templateUrl di una direttiva personalizzata che avevo nel mio progetto. A causa dell'errore di battitura, non è stato possibile caricare il modello.

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

Guarda nella scheda Rete degli strumenti di sviluppo del tuo browser web e controlla se qualche risorsa ha un errore 404.

Facile da trascurare, perché il messaggio di errore è molto criptico e apparentemente non correlato al vero problema.


0

Stavo riscontrando questo problema nel mio progetto perché .otherwise () mancava la definizione del percorso e stavo percorrendo un percorso sbagliato.


0

Ho avuto questo problema perché lo stavo facendo

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

Invece di questo: (avviso = vs ===), il mio unit test ha iniziato a rompersi e ho scoperto la mia stupidità

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

0

Mi sono imbattuto in questo problema in cui avevo bisogno di un tooltip dinamico ... ha causato angular a ricalcolarlo ogni volta come un nuovo valore (anche se era lo stesso). Ho creato una funzione per memorizzare nella cache il valore calcolato in questo modo:

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

Quindi nell'html, l'ho acceduto in questo modo:

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

0

ecco come mi sono avvicinato e ho trovato una soluzione: ho controllato il testo, ha mostrato:

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

Watchers attivati ​​nelle ultime 5 iterazioni: [[{"msg": "statement === statment && functionCall ()", "newVal": [{"id": 7287, "referen ...

quindi se puoi vedere il file

msg

questa è l'affermazione che genera l'errore. Ho controllato la funzione chiamata in questo messaggio, ho restituito (false) da tutti loro solo per determinare quale ha il problema. uno di loro stava chiamando una funzione che continua a cambiare il ritorno, che è il problema.


-3

Per quanto folle possa sembrare, ho risolto questo errore semplicemente riavviando il browser quando si è verificato all'improvviso.

Quindi una soluzione è semplicemente svuotare la cache del browser o provare a riavviare il browser.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.