Como solucionar problemas de erro angular “10 $ digest () iterações atingidas”

91

10 $ digest () iterações alcançadas. Abortando!

Há muito texto de suporte no sentido de "Observadores acionados nas últimas 5 iterações:", etc., mas muito desse texto é código Javascript de várias funções. Existem regras básicas para diagnosticar este problema? É um problema que SEMPRE pode ser mitigado ou há aplicativos complexos o suficiente para que esse problema seja tratado apenas como um aviso?

blaster
fonte

Respostas:

80

como Ven disse, ou você está retornando objetos diferentes (não idênticos) em cada $digestciclo, ou está alterando os dados muitas vezes.

A solução mais rápida para descobrir qual parte do seu aplicativo está causando esse comportamento é:

  1. remova todos os HTML suspeitos - basicamente remova todo o seu html do modelo e verifique se não há avisos
  2. se não houver avisos - adicione pequenas partes do html que você removeu e verifique se o problema voltou
  3. repita o passo 2 até obter um aviso - você descobrirá qual parte do seu html é responsável pelo problema
  4. investigue mais - a parte da etapa 3 é responsável por transformar os objetos no $scopeou por retornar objetos não idênticos em cada $digestciclo.
  5. se você ainda tiver $digestavisos de iteração após a etapa 1, provavelmente está fazendo algo muito suspeito. Repita as mesmas etapas para o modelo / escopo / controlador pai

Você também quer ter certeza de não estar alterando a entrada de seus filtros personalizados

Lembre-se de que em JavaScript existem tipos específicos de objetos que não se comportam como você normalmente esperaria:

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
fonte
5
Obrigado! Esta é uma heurística útil. Também estou pensando que o novo recurso "rastrear por" do Angular para o ngRepeat vai me ajudar também. Estou fazendo algumas coisas de map () e groupBy () usando Underscore em um serviço, então ele definitivamente retorna objetos "diferentes" a cada vez (embora eles representem logicamente as mesmas coisas - "rastrear por Id" ajudaria o Angular a não vê-los como uma "mudança" se não o fizeram).
blaster de
2
Se você estiver fazendo map()e groupBy()certifique-se de que seus equipamentos $watchrealizem uma verificação suja, objectEquality $watch('myFunctionDoingGropby',callback,true) consulte docs.angularjs.org/api/ng.$rootScope.Scope#$watch
g00fy
Conforme o comentário acima, "rastrear por" corrigiu meu problema (veja os documentos aqui: docs.angularjs.org/api/ng/directive/ngRepeat ). Eu ficaria grato por um comentário explicativo explicando porque isso funciona ...
Soferio
@ g00fy: Essa foi uma boa pista, e fui capaz de substituir minha função de recuperação de lista por uma variável na minha $scopeque é atribuída à _.maplista -ed uma vez - mas, no caso geral, como você influenciaria essa verificação suja por igualdade de objeto se não é um criado manualmente $watchque está tropeçando, mas ngRepeat?
OR Mapper
73

Normalmente isso acontece quando você retorna um objeto diferente a cada vez.

Por exemplo, se você usar isso em um ng-repeat:

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

Você receberá esta mensagem de erro porque o Angular tenta ter a "estabilidade" e executará a função até retornar o mesmo resultado 2 vezes (comparando com ===), o que em nosso caso nunca retornará verdadeiro porque a função sempre retorna um novo objeto.

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

Neste caso, você pode corrigi-lo armazenando o objeto no escopo diretamente, por exemplo

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

Assim você sempre retorna o mesmo objeto!

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

(Você nunca deve encontrar isso, mesmo em aplicativos complexos).

Atualização: Angular adicionou algumas explicações mais detalhadas em seu site .

Ven
fonte
Como você impede que isso ocorra? Estou passando por uma situação semelhante agora.
Conqueror
2
Certifique-se de não criar objetos diferentes em cada chamada;).
Ven
Como posso garantir isso? Estou fazendo algo como item in func (obj), mas func parece ser chamado muitas vezes em vez de uma vez, como espero. Tentei usar ng-init para chamar func e, em seguida, anexá-lo a um modelo no escopo, mas também não funcionou.
Conqueror
1
Este é o exemplo que você vê em todos os lugares sobre este problema. Seria tão incrível se o Angular pudesse apontar para a função ofensiva, que tem este comportamento ...
Jorn
1
@BenWheeler Certamente melhorou desde 2013, sim: P.
Ven
11

Só queria jogar essa solução aqui, espero que ajude outras pessoas. Eu estava tendo esse problema de iteração porque estava iterando sobre uma propriedade gerada que fazia um novo objeto toda vez que era chamada.

Corrigi-o armazenando em cache o objeto gerado na primeira vez que foi solicitado e, em seguida, sempre retornando o cache, se ele existisse. Um método dirty () também foi adicionado, o que destruiria os resultados armazenados em cache conforme necessário.

Eu tinha algo assim:

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

            return retObj;
        }
    });
}

E aqui está a solução implementada:

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

            return _cached;
        }
    });

    myObj.dirty = function () {
        _cached = null;
    }
}
Asmor
fonte
Oh meu Deus, eu gostaria de poder te dar 10 votos positivos. Você acabou de resolver um problema que me deixou batendo minha cabeça contra a parede por quase 3 horas. Eu te amo!
GMA de
7

Também existe a possibilidade de não ser um loop infinito. 10 iterações não é um número suficientemente grande para concluir isso com alguma certeza. Portanto, antes de partir para uma caçada selvagem, é aconselhável descartar essa possibilidade primeiro.

O método mais fácil de fazer isso é aumentar a contagem máxima do loop de resumo para um número muito maior, o que pode ser feito no module.configmétodo, usando o $rootScopeProvider.digestTtl(limit)método. Se oinfdig erro não aparecer mais, você simplesmente tem alguma lógica de atualização suficientemente complexa.

Se você construir dados ou visualizações com base em relógios recursivos, você pode querer procurar soluções iterativas (ou seja, não depender de novos loops de resumo para serem iniciados) usando while, forouArray.forEach . Às vezes, a estrutura é altamente aninhada e nem mesmo recursiva, provavelmente não há muito a ser feito nesses casos, exceto aumentar o limite.

Outro método de depurar o erro é examinar os dados de resumo. Se você imprimir o JSON, obterá uma série de matrizes. Cada entrada de nível superior representa uma iteração, cada iteração consiste em uma lista de entradas de observação.

Se você, por exemplo, tem uma propriedade que é modificada em $watchsi mesma, é fácil ver que o valor está mudando 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
      }
   ]
]

Claro que em projetos maiores isso pode não ser tão simples, especialmente porque o msgcampo geralmente tem o valor "fn: regularInterceptedExpression"se o relógio for um{{ }} interpolação.

Além disso, os métodos já mencionados, como cortar o HTML para encontrar a origem do problema, são obviamente úteis.

HB
fonte
6

Eu tive o mesmo problema - estava criando uma nova data toda vez. Então, para qualquer pessoa que lida com datas, converti todas as chamadas como esta:

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

para:

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

Inicializar um número em vez de um objeto de data resolveu para mim.

Arman Bimatov
fonte
2

a maneira mais fácil é: use angular.js, não o arquivo min. abra e encontre a linha:

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

adicione a linha abaixo:

console.log("aaaa",watch)

e, em seguida, atualize sua página, no console de ferramentas de desenvolvimento, você encontrará seu código de erro.

perguntar
fonte
0

Também gostaria de mencionar que recebi essa mensagem de erro quando cometi um erro de digitação no templateUrl de uma diretiva personalizada que tinha em meu projeto. Devido ao erro de digitação, o modelo não pôde ser carregado.

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

Procure na guia de rede das ferramentas de desenvolvimento do seu navegador da web e veja se algum recurso está apresentando um erro 404.

Fácil de ignorar, porque a mensagem de erro é muito enigmática e aparentemente não está relacionada ao problema real.

Casey Plummer
fonte
0

Eu estava tendo esse problema em meu projeto porque o. Caso contrário () estava faltando na minha definição de rota e eu estava seguindo a rota errada.

Sandeep M
fonte
0

Tive esse problema porque estava fazendo isso

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

Em vez disso: (aviso = vs ===), meu teste de unidade começou a falhar e descobri minha estupidez

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

Encontrei esse problema em que precisava de uma dica de ferramenta dinâmica ... fazia com que o angular o recalculasse sempre como um novo valor (embora fosse o mesmo). Eu criei uma função para armazenar em cache o valor calculado assim:

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

Então, no html, acessei assim:

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

foi assim que abordei e encontrei uma solução: verifiquei o texto, mostrou:

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

Watchers disparados nas últimas 5 iterações: [[{"msg": "statement === statment && functionCall ()", "newVal": [{"id": 7287, "referen ...

então se você pode ver o

msg

essa é a instrução que gera o erro. Verifiquei a função chamada nesta mensagem, retornei (false) de todos eles apenas para determinar qual deles tem o problema. um deles estava chamando uma função que fica mudando o retorno, que é o problema.

Ahmed M. Matar
fonte
-3

Por mais louco que pareça, eu corrigi esse erro apenas reiniciando meu navegador quando ele apareceu de repente.

Portanto, uma solução é apenas limpar o cache do seu navegador ou tentar reiniciá-lo.

cst1992
fonte