Как устранить ошибку Angular «Достигнуто 10 итераций $ digest ()»

91

Достигнуто 10 итераций $ digest (). Прерывание!

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

бластер
источник

Ответы:

80

как сказал Вен, вы либо возвращаете разные (не идентичные) объекты в каждом $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
g00fy
источник
5
Спасибо! Это полезный эвристический метод. Я также думаю, что новая функция Angular "track by" для ngRepeat мне тоже поможет. Я делаю некоторые вещи map () и groupBy (), используя Underscore в сервисе, поэтому он определенно возвращает "разные" объекты каждый раз (даже если они логически представляют одни и те же вещи - "track by Id" поможет Angular их не видеть как "изменение", если они этого не сделали).
Blaster
2
Если вы это делаете map()и groupBy()убедитесь, что ваши $watches выполняют грязную проверку, objectEquality $watch('myFunctionDoingGropby',callback,true) см. Docs.angularjs.org/api/ng.$rootScope.Scope#$watch
g00fy
Согласно приведенному выше комментарию, «отслеживание по» устранило мою проблему (см. Документы здесь: docs.angularjs.org/api/ng/directive/ngRepeat ). Буду благодарен за пояснительный комментарий, объясняющий, почему это работает ...
Соферио
@ g00fy: Это было хорошее зацепление, и я смог заменить свою функцию поиска списка переменной на моем, $scopeкоторой _.mapодин раз назначается список -ed, но в общем случае, как бы вы повлияли на указанную грязную проверку равенством объектов, если это не созданный вручную, $watchкоторый отключается, но ngRepeat?
OR Mapper
73

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

Например, если вы используете это в 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 добавил на свой сайт более подробное объяснение .

Ven
источник
Как предотвратить это? У меня сейчас похожая ситуация.
Conqueror
2
Убедитесь, что вы не создаете разные объекты при каждом вызове;).
Ven
Как я могу в этом убедиться? Я делаю что-то вроде item in func (obj), но кажется, что func вызывается много раз, а не один раз, как я надеюсь. Я попытался использовать ng-init для вызова func, а затем прикрепить его к модели в области видимости, но это тоже не сработало.
Conqueror
1
Это пример, который вы видите повсюду по этой проблеме. Было бы так здорово, если бы Angular мог указать на вызывающую нарушение функция, которая имеет такое поведение ...
Йорн
1
@BenWheeler Он определенно улучшился с 2013 года, да: P.
Вен,
11

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

Я исправил это, кэшируя сгенерированный объект при первом запросе, а затем всегда возвращая кеш, если он существует. Также был добавлен метод 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 голосов. Вы только что решили проблему, из-за которой я почти 3 часа бился головой о стену. Я люблю тебя!
GMA
7

Также существует вероятность того, что это вообще не бесконечный цикл. 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 для поиска источника проблемы, конечно же, полезны.

HB
источник
6

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

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

кому:

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

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

Арман Биматов
источник
2

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

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

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

console.log("aaaa",watch)

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

аски
источник
0

Я также хотел бы отметить, что я получил это сообщение об ошибке, когда у меня была опечатка в 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.

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

Кейси Пламмер
источник
0

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

Сандип М
источник
0

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

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

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

var variableExpense = this.lodash.find(product.variableExpenseList, (ve) => {
               return ve.rawMaterial.id === rawMaterial.id;
});
Маккурт
источник
0

Я столкнулся с этой проблемой, когда мне нужна была динамическая всплывающая подсказка ... это заставляло 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">
Scrappy Doo
источник
0

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

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

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

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

сообщение

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

Ахмед М. Матар
источник
-3

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

Итак, одно из решений - просто очистить кеш браузера или попробовать перезапустить браузер.

cst1992
источник