Cómo solucionar el error angular "10 $ digest () iteraciones alcanzadas"

91

Se alcanzaron 10 iteraciones de $ digest (). ¡Abortando!

Hay mucho texto de apoyo en el sentido de "Vigilantes despedidos en las últimas 5 iteraciones:", etc., pero gran parte de este texto es código Javascript de varias funciones. ¿Existen reglas generales para diagnosticar este problema? ¿Es un problema que SIEMPRE se puede mitigar, o hay aplicaciones lo suficientemente complejas como para tratar este problema como una advertencia?

desintegrador
fuente

Respuestas:

80

como dijo Ven, o está devolviendo objetos diferentes (no idénticos) en cada $digestciclo, o está alterando los datos demasiadas veces.

La solución más rápida para averiguar qué parte de su aplicación está causando este comportamiento es:

  1. elimine todo el HTML sospechoso: básicamente elimine todo su html de la plantilla y verifique si no hay advertencias
  2. si no hay advertencias, agregue pequeñas partes del html que eliminó y verifique si el problema ha vuelto
  3. repita el paso 2 hasta que reciba una advertencia: averiguará qué parte de su html es responsable del problema
  4. investigar más a fondo: la parte del paso 3 es responsable de mutar los objetos en el $scopeo de devolver objetos no idénticos en cada $digestciclo.
  5. Si todavía tiene $digestadvertencias de iteración después del paso 1, probablemente esté haciendo algo muy sospechoso. Repita los mismos pasos para la plantilla / alcance / controlador principal

También desea asegurarse de no alterar la entrada de sus filtros personalizados

Tenga en cuenta que en JavaScript hay tipos específicos de objetos que no se comportan como normalmente esperaría:

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
fuente
5
¡Gracias! Esta es una heurística útil. También estoy pensando que la nueva función de "seguimiento por" de Angular para ngRepeat también me ayudará. Estoy haciendo algunas cosas de map () y groupBy () usando Underscore en un servicio, por lo que definitivamente devuelve objetos "diferentes" cada vez (aunque lógicamente representan las mismas cosas, "rastrear por Id" ayudaría a Angular a no verlos como un "cambio" si realmente no lo han hecho).
bláster
2
Si lo está haciendo map()y luego groupBy()asegúrese de que su $watches realice una verificación sucia, objectEquality $watch('myFunctionDoingGropby',callback,true) consulte docs.angularjs.org/api/ng.$rootScope.Scope#$watch
g00fy
Según el comentario anterior, "track by" solucionó mi problema (consulte los documentos aquí: docs.angularjs.org/api/ng/directive/ngRepeat ). Agradecería un comentario explicativo que explique por qué funciona esto ...
Soferio
@ g00fy: Esa fue una buena ventaja, y pude reemplazar mi función de recuperación de lista con una variable en mi a la $scopeque se le asigna la _.maplista -ed una vez, pero en el caso general, ¿cómo influiría en dicha verificación sucia por igualdad de objeto si no es un creado manualmente $watchque se está disparando, pero ngRepeat?
OR Mapper
73

Por lo general, eso sucede cuando devuelve un objeto diferente cada vez.

Por ejemplo, si usa esto en un ng-repeat:

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

Obtendrá este mensaje de error porque Angular intenta tener la "estabilidad" y ejecutará la función hasta que devuelva el mismo resultado 2 veces (comparando con ===), que en nuestro caso nunca devolverá verdadero porque la función siempre devuelve un nuevo objeto.

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

En este caso, puede solucionarlo almacenando el objeto en el alcance directamente, por ejemplo

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

¡De esa forma siempre devolverás el mismo objeto!

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

(Nunca debería encontrar eso, incluso en aplicaciones complejas).

Actualización: Angular ha agregado una explicación más detallada en su sitio web .

Ven
fuente
¿Cómo evitas que esto ocurra? Estoy teniendo una situación similar en este momento.
Conquistador
2
Asegúrese de no crear objetos diferentes en cada llamada;).
Ven
¿Cómo puedo asegurar esto? Estoy haciendo algo como item in func (obj), pero parece que func se llama muchas veces en lugar de una vez, como espero. Traté de usar ng-init para llamar a func y luego adjuntarlo a un modelo en el alcance, pero eso tampoco funcionó.
Conquistador
1
Este es el ejemplo que ve en todas partes sobre este problema. Sería increíble si Angular pudiera apuntar a la función ofensiva, que tiene este comportamiento ...
Jorn
1
@BenWheeler Ciertamente mejoró desde 2013, sí: P.
Ven
11

Solo quería lanzar esta solución aquí, espero que ayude a otros. Obtuve este problema de iteración porque estaba iterando sobre una propiedad generada que estaba creando un nuevo objeto cada vez que se llamaba.

Lo arreglé almacenando en caché el objeto generado la primera vez que se solicitó y luego siempre devolviendo la caché si existía. También se agregó un método sucio (), que destruiría los resultados almacenados en caché según fuera necesario.

Tuve algo como esto:

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

            return retObj;
        }
    });
}

Y aquí está con la solución implementada:

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

            return _cached;
        }
    });

    myObj.dirty = function () {
        _cached = null;
    }
}
Asmor
fuente
Dios mío, desearía poder darte 10 votos a favor. Acabas de resolver un problema que me hizo golpearme la cabeza contra la pared durante casi 3 horas. ¡Te quiero!
GMA
7

También existe la posibilidad de que no sea un bucle infinito en absoluto. Diez iteraciones no es un número suficientemente grande para concluirlo con cierta certeza. Por lo tanto, antes de emprender una búsqueda inútil, puede ser aconsejable descartar esa posibilidad primero.

El método más fácil para hacerlo es aumentar el recuento máximo de bucles de resumen a un número mucho mayor, lo que se puede hacer en el module.configmétodo, utilizando el $rootScopeProvider.digestTtl(limit)método. Si el infdigerror ya no aparece, simplemente tiene una lógica de actualización lo suficientemente compleja.

Si se construye de datos o puntos de vista que dependen de relojes recursivas es posible que desee buscar soluciones iterativas (es decir, no depender de nuevos bucles de digerir para ser iniciado) usando while, foro Array.forEach. A veces, la estructura está muy anidada y ni siquiera es recursiva, probablemente no haya mucho que hacer en esos casos, excepto elevar el límite.

Otro método para depurar el error es mirar los datos del resumen. Si imprime bastante el JSON, obtiene una matriz de matrices. Cada entrada de nivel superior representa una iteración, cada iteración consta de una lista de entradas de vigilancia.

Si, por ejemplo, tiene una propiedad que se modifica en $watchsí misma, es fácil ver que el valor cambia infinitamente:

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

Por supuesto, en proyectos más grandes esto puede no ser tan simple, especialmente porque el msgcampo a menudo tiene el valor "fn: regularInterceptedExpression"si el reloj es una {{ }}interpolación.

Aparte de eso, los métodos ya mencionados, como reducir el HTML para encontrar la fuente del problema, son por supuesto útiles.

media pensión
fuente
6

Tuve el mismo problema: estaba creando una nueva fecha cada vez. Entonces, para cualquiera que se ocupe de fechas, convertí todas las llamadas de esta manera:

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

a:

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

Inicializar un número en lugar de un objeto de fecha lo resolvió para mí.

Arman Bimatov
fuente
2

la forma fácil es: use angular.js, no el archivo min. ábrelo y encuentra la línea:

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

agregue la línea a continuación:

console.log("aaaa",watch)

y luego actualice su página, en la consola de herramientas de desarrollo, encontrará su código de error.

askie
fuente
0

También me gustaría mencionar que recibí este mensaje de error cuando tuve un error tipográfico en el templateUrl de una directiva personalizada que tenía en mi proyecto. Debido al error tipográfico, no se pudo cargar la plantilla.

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

Busque en la pestaña de red de las herramientas de desarrollo de su navegador web y observe si algún recurso tiene un error 404.

Fácil de pasar por alto, porque el mensaje de error es muy críptico y aparentemente no está relacionado con el problema real.

Casey Plummer
fuente
0

Tenía este problema en mi proyecto porque al .otherwise () le faltaba la definición de mi ruta y estaba tomando la ruta incorrecta.

Sandeep M
fuente
0

Tuve este problema porque estaba haciendo esto

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

En lugar de esto: (aviso = vs ===), mi prueba unitaria comenzó a fallar y encontré mi estupidez

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

Me encontré con este problema en el que necesitaba una información sobre herramientas dinámica ... hizo que angular lo recalculara cada vez como un nuevo valor (aunque era el mismo). Creé una función para almacenar en caché el valor calculado así:

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

Luego, en el html, accedí así:

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

así es como lo abordé y encontré una solución: revisé el texto, mostraba:

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

Los observadores dispararon en las últimas 5 iteraciones: [[{"msg": "statement === statment && functionCall ()", "newVal": [{"id": 7287, "referen ...

así que si puedes ver el

msg

esa es la declaración que genera el error. Revisé la función llamada en este mensaje, devolví (falso) de todos ellos solo para determinar cuál tiene el problema. uno de ellos estaba llamando a una función que sigue cambiando el retorno, que es el problema.

Ahmed M. Matar
fuente
-3

Por loco que parezca, solucioné este error con solo reiniciar mi navegador cuando apareció de repente.

Entonces, una solución es simplemente borrar la memoria caché de su navegador o intentar reiniciar el navegador.

cst1992
fuente