Как устранить неполадки угловой ошибки 10 $digest()

10 $digest() достигнуты итерации. Aborting!

Существует много вспомогательного текста в смысле "Наблюдатели сработали за последние 5 итераций: " и т. Д., Но большая часть этого текста представляет собой код Javascript из различных функций. Существуют ли практические правила для диагностики этой проблемы? Это проблема, которую ВСЕГДА можно смягчить, или существуют достаточно сложные приложения, чтобы эту проблему можно было рассматривать как предупреждение?

12 ответов

Решение

Как сказал Вен, вы либо возвращаете разные (не идентичные) объекты на каждом $digest цикл, или вы изменяете данные слишком много раз.

Самое быстрое решение, чтобы выяснить, какая часть вашего приложения вызывает такое поведение:

  1. удалить все подозрительные HTML - в основном удалите все ваши HTML из шаблона и проверьте, нет ли предупреждений
  2. если предупреждений нет - добавьте небольшие фрагменты удаленного html и проверьте, не исчезла ли проблема
  3. повторяйте шаг 2, пока не получите предупреждение - вы выясните, какая часть вашего html отвечает за проблему
  4. исследовать дальше - часть из шага 3 отвечает за мутирование объектов на $scope или возвращает неидентичные объекты на каждом $digest цикл.
  5. если у вас еще есть $digest итерационные предупреждения после шага 1, чем вы, вероятно, делаете что-то очень подозрительное. Повторите те же шаги для родительского шаблона / области / контроллера

Вы также хотите убедиться, что вы не изменяете ввод ваших пользовательских фильтров

Имейте в виду, что в JavaScript есть определенные типы объектов, которые ведут себя не так, как вы обычно ожидаете:

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

Обычно это происходит, когда вы каждый раз возвращаете новый объект.

Например, если вы используете это в ng-repeat:

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

Вы получите это сообщение об ошибке, потому что Angular пытается получить "стабильность" и будет выполнять функцию до тех пор, пока не вернет тот же результат 2 раза (по сравнению с ===), который в нашем случае никогда не вернет true, потому что функция всегда возвращает новый объект.

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

В этом случае вы можете исправить это, сохранив объект непосредственно в области видимости, например,

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

Таким образом, вы всегда возвращаете один и тот же объект!

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

(Вы никогда не должны сталкиваться с этим, даже в сложных приложениях).

Обновление: Angular добавил более подробное объяснение на свой веб-сайт.

Просто хотел добавить это решение сюда, надеюсь, оно поможет другим. Я получил эту проблему итерации, потому что я перебирал сгенерированное свойство, которое создавало новый объект каждый раз, когда он вызывался.

Я исправил это, кэшируя сгенерированный объект при первом запросе, а затем всегда возвращал кеш, если он существовал. Также был добавлен метод dirty(), который при необходимости уничтожал бы кэшированные результаты.

У меня было что-то вроде этого:

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

            return retObj;
        }
    });
}

И вот с реализованным решением:

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

            return _cached;
        }
    });

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

Также существует возможность того, что это вообще не бесконечный цикл. 10 итераций - не достаточно большое число, чтобы сделать вывод с какой-либо степенью достоверности. Поэтому, прежде чем отправиться в погоню за диким гусем, рекомендуется сначала исключить такую ​​возможность.

Самый простой способ сделать это - увеличить максимальное число циклов дайджеста до гораздо большего числа, что можно сделать в module.config метод, используя $rootScopeProvider.digestTtl(limit) метод. Если infdig ошибка больше не появляется, у вас просто есть достаточно сложная логика обновления.

Если вы строите данные или представления, опираясь на рекурсивные наблюдения, вы можете искать итеративные решения (т.е. не полагаясь на новые циклы дайджеста, которые нужно запустить), используя while, for или же Array.forEach, Иногда структура просто сильно вложена и даже не рекурсивна, в этих случаях, вероятно, мало что можно сделать, кроме повышения лимита.

Другой метод отладки ошибки - просмотр дайджест-данных. Если вы просто печатаете JSON, вы получаете массив массивов. Каждая запись верхнего уровня представляет собой итерацию, каждая итерация состоит из списка записей наблюдения.

Если у вас, например, есть свойство, которое изменяется в $watch Само по себе легко увидеть, что значение меняется бесконечно:

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

Конечно, в более крупном проекте это может быть не так просто, тем более что msg поле часто имеет значение "fn: regularInterceptedExpression" если часы {{ }} интерполяция.

Кроме того, уже упомянутые методы, такие как сокращение HTML, чтобы найти источник проблемы, конечно, полезны.

У меня была та же проблема - я каждый раз создавал новую дату. Поэтому для тех, кто имеет дело с датами, я конвертировал все звонки так:

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

чтобы:

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

Инициализация числа вместо объекта даты решила это для меня.

Самый простой способ: использовать angular.js, а не файл min. откройте его и найдите строку:

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

добавьте строку ниже:

console.log("aaaa",watch)

а затем обновите страницу, в консоли инструментов разработки вы найдете код ошибки.

Это известная ошибка в ui-router, это помогло нам: https://github.com/angular-ui/ui-router/issues/600

Я также хотел бы упомянуть, что я получил это сообщение об ошибке, когда у меня была опечатка в templateUrl пользовательской директивы, которая была в моем проекте. Из-за опечатки не удалось загрузить шаблон.

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

Посмотрите на вкладку сети инструментов разработчика вашего веб-браузера и посмотрите, есть ли на каком-либо ресурсе ошибка 404.

Легко пропустить, потому что сообщение об ошибке очень загадочно и, по-видимому, не связано с реальной проблемой.

У меня была эта проблема, потому что я делал это

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

Вместо этого: (обратите внимание = против ===) мой модульный тест начал ломаться, и я обнаружил свою глупость

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

Вот как я подошел к нему и нашел решение: я проверил текст, он показал:

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

Наблюдатели сработали за последние 5 итераций: [[{"msg":"оператор === statment && functionCall()","newVal":[{"id":7287,"referen...

так что если вы можете увидеть

тзд

это утверждение, генерирующее ошибку. Я проверил функцию, вызванную в этом сообщении, я возвратил (false) от всех из них, просто чтобы определить, какая из них имеет проблему. один из них вызывал функцию, которая постоянно изменяет возвращаемый результат, что является проблемой.

Я столкнулся с этой проблемой, когда мне потребовалась динамическая всплывающая подсказка... из-за того, что angular пересчитывал ее каждый раз как новое значение (даже если оно было одинаковым). Я создал функцию для кэширования вычисленного значения следующим образом:

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

Затем в HTML, я получил к нему доступ так:

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

У меня была эта проблема в моем проекте, потому что .otherwise() пропустил мое определение маршрута, и я выбрал неправильный маршрут.

Как бы безумно это ни звучало, я исправил эту ошибку, просто перезапустив свой браузер, когда он неожиданно неожиданно появился.

Поэтому одним из решений является просто очистить кеш вашего браузера или попробовать перезапустить браузер.

Другие вопросы по тегам