Як усунути неполадки при кутовій помилці “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

5
Дякую! Це корисна евристика. Я також думаю, що нова функція Angular "відстеження за" для ngRepeat також допоможе мені. Я роблю деякі матеріали map () та groupBy (), використовуючи Underscore в службі, тому він, безумовно, щоразу повертає "різні" об'єкти (хоча вони логічно представляють одне і те ж - "track by Id" допомогло б Angular їх не бачити як "зміна", якщо вони насправді цього не зробили).
бластер

2
Якщо ви робите це, map()а потім groupBy()переконайтесь, що ваші есе$watch виконують брудні перевірки, 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?
АБО Mapper

73

Зазвичай це трапляється, коли ви щоразу повертаєте інший предмет.

Наприклад, якщо ви використовуєте це в ng-repeat:

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

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

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 додав ще кілька поглиблених пояснень на своєму веб-сайті .


Як зупинити це? Зараз у мене схожа ситуація.
Conqueror

2
Переконайтесь, що ви не створюєте різних об’єктів під час кожного дзвінка;).
вен

Як я можу це забезпечити? Я роблю щось на зразок елемента у 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 для пошуку джерела проблеми.


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

У мене виникла ця проблема у моєму проекті, оскільки в іншому випадку () відсутнє моє визначення маршруту, і я потрапив не так.


0

У мене була ця проблема, тому що я цим займався

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

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">

0

ось як я підійшов до цього і знайшов рішення: я перевірив текст, він показав:

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

Спостерігачі спрацювали за останні 5 ітерацій: [[{{msg ":" statement === statment && functionCall () "," newVal ": [{" id ": 7287," reference ...

так що якщо ви можете побачити

повідомлення

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


-3

Як би це не звучало божевільно, я виправив цю помилку, просто перезапустивши браузер, коли він раптово з’явився.

Тож одним із рішень є просто очистити кеш браузера або спробувати перезапустити браузер.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.