95 votes

Comment résoudre l'erreur Angular "10 itérations $digest() atteintes" ?

10 itérations de $digest() atteintes. Abandon !

Il y a beaucoup de texte justificatif dans le sens de " Les veilleurs ont tiré dans les 5 dernières itérations : "etc., mais une grande partie de ce texte est du code Javascript provenant de diverses fonctions. Existe-t-il des règles empiriques pour diagnostiquer ce problème ? S'agit-il d'un problème qui peut TOUJOURS être atténué, ou existe-t-il des applications suffisamment complexes pour que ce problème soit traité comme un simple avertissement ?

84voto

g00fy Points 1777

Comme Ven l'a dit, soit vous renvoyez des objets différents (non identiques) sur chaque $digest cycle, ou vous modifiez les données trop souvent.

La solution la plus rapide pour déterminer quelle partie de votre application est à l'origine de ce comportement est la suivante :

  1. supprimez tout le HTML suspect - en gros, supprimez tout le HTML du modèle, et vérifiez s'il n'y a pas d'avertissement.
  2. s'il n'y a pas d'avertissement - ajoutez de petites parties du html que vous avez supprimé et vérifiez si le problème est de retour
  3. répétez l'étape 2 jusqu'à ce que vous obteniez un avertissement - vous découvrirez ainsi quelle partie de votre html est responsable du problème
  4. approfondir - la partie de l'étape 3 est responsable de la mutation des objets sur le serveur de l'entreprise. $scope ou renvoie des objets non identiques à chaque $digest cycle.
  5. si vous avez encore $digest des avertissements d'itération après l'étape 1, alors vous faites probablement quelque chose de très suspect. Répétez les mêmes étapes pour le modèle parent/scope/controller

Vous devez également vous assurer que vous ne modifiez pas l'entrée de vos filtres personnalisés.

N'oubliez pas qu'en JavaScript, il existe des types d'objets spécifiques qui ne se comportent pas comme vous l'attendez normalement :

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

75voto

Ven Points 7349

Cela se produit généralement lorsque l'on renvoie un objet différent à chaque fois.

Par exemple, si vous l'utilisez dans un ng-repeat :

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

Vous allez obtenir ce message d'erreur car Angular essaie d'avoir la "stabilité" et va exécuter la fonction jusqu'à ce qu'elle renvoie le même résultat 2 fois (en comparant avec === ), qui dans notre cas ne retournera jamais vrai car la fonction retourne toujours un nouvel objet.

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

Dans ce cas, vous pouvez résoudre le problème en stockant directement l'objet dans le champ d'application, par ex.

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

De cette façon, vous retournez toujours le même objet !

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

(Vous ne devriez jamais rencontrer cela, même sur des applications complexes).

Mise à jour : Angular a ajouté des explications plus approfondies sur son site web .

11voto

Asmor Points 1693

Je voulais juste proposer cette solution, en espérant qu'elle puisse aider d'autres personnes. J'avais ce problème d'itération parce que j'itérais sur une propriété générée qui créait un nouvel objet à chaque fois qu'elle était appelée.

J'ai corrigé le problème en mettant en cache l'objet généré la première fois qu'il était demandé, puis en retournant toujours le cache s'il existait. Une méthode dirty() a également été ajoutée, qui détruit les résultats mis en cache si nécessaire.

J'avais quelque chose comme ça :

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

            return retObj;
        }
    });
}

Et voici la solution mise en œuvre :

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

            return _cached;
        }
    });

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

7voto

H.B. Points 76352

Il est également possible que ce ne soit pas du tout une boucle infinie. 10 itérations n'est pas un nombre suffisamment important pour conclure avec certitude. Avant de se lancer dans une course folle, il est donc préférable d'écarter d'abord cette possibilité.

La méthode la plus simple pour y parvenir est d'augmenter le nombre maximal de boucles de résumé à un nombre beaucoup plus grand, ce qui peut être fait dans l'interface de l'utilisateur. module.config en utilisant la méthode $rootScopeProvider.digestTtl(limit) méthode. Si le infdig n'apparaît plus, vous avez simplement une logique de mise à jour suffisamment complexe.

Si vous construisez des données ou des vues reposant sur des montres récursives, vous pouvez rechercher des solutions itératives (c'est-à-dire ne reposant pas sur le lancement de nouvelles boucles de digestion) en utilisant la méthode suivante while , for o Array.forEach . Parfois, la structure est simplement très imbriquée et pas même récursive, il n'y a probablement pas grand-chose à faire dans ces cas-là, si ce n'est augmenter la limite.

Une autre méthode de débogage de l'erreur consiste à examiner les données du condensé. Si vous imprimez le JSON, vous obtenez un tableau de tableaux. Chaque entrée de niveau supérieur représente une itération, chaque itération consiste en une liste d'entrées de veille.

Si vous avez par exemple une propriété qui est modifiée dans une $watch sur lui-même, il est facile de voir que la valeur change infiniment :

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

Bien sûr, dans un projet de plus grande envergure, cela peut ne pas être aussi simple, d'autant plus que l'équipe de la msg a souvent la valeur "fn: regularInterceptedExpression" si la montre est un {{ }} interpolation.

En dehors de cela, les méthodes déjà mentionnées, comme le découpage du HTML pour trouver la source du problème, sont bien sûr utiles.

6voto

Arman Bimatov Points 461

J'avais le même problème - je créais une nouvelle date à chaque fois. Donc, pour tous ceux qui traitent avec des dates, j'ai converti tous les appels comme ceci :

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

à :

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

Initialiser un nombre au lieu d'un objet date a résolu le problème pour moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X