如何解决Angular“达到10次$ digest()迭代”错误


91

达到10个$ digest()迭代。流产!

有很多支持文本,例如“在最近5个迭代中触发了Watchers:”等,但是其中很多文本是来自各种功能的Javascript代码。有诊断这个问题的经验法则吗?是可以始终缓解的问题,还是应用程序足够复杂以至于只能将此问题视为警告?

Answers:


80

正如Ven所说,您或者在每个$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提供的新“ track by”功能也将对我有所帮助。我在服务中使用Underscore在做一些map()和groupBy()事情,所以每次肯定返回“不同”的对象(即使它们在逻辑上表示相同的东西-“按ID跟踪”将帮助Angular看不到它们) (如果不是的话)作为“更改”)。
blaster 2013年

2
如果您正在做,map()groupBy()不是确保自己的$watches执行脏检查,objectEquality $watch('myFunctionDoingGropby',callback,true) 请参阅docs.angularjs.org/api/ng.$ro​​otScope.Scope#$watch
g00fy 2013年

按照上面的评论,“跟踪依据”解决了我的问题(请参见此处的文档:docs.angularjs.org/api/ng/directive/ngRepeat)。我将不胜感激,以解释性的解释来解释为什么这行得通...
Soferio'1

@ g00fy:这是一个很好的线索,我能够用我的变量替换我的列表检索功能,该变量一次$scope被分配了_.map-ed列表-但是在一般情况下,如果对象相等,您如何影响所说的脏检查?不是手动创建的$watch,而是ngRepeat
或Mapper

73

通常,这种情况发生在每次返回一个不同的对象时。

例如,如果您在ng-repeat

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

您将收到此错误消息,因为Angular尝试具有“稳定性”并将执行函数,直到它返回相同的结果2次(与相比===),在我们的情况下,由于函数始终返回a,因此永远不会返回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 在其网站上添加了一些更深入的解释


您如何阻止这种情况的发生?我现在也有类似情况。
征服者2013年

2
确保您没有在每个调用中创建不同的对象;)。
2013年

我该如何确保?我正在做func(obj)中的item之类的事情,但是func似乎被多次调用,而不是像我希望的那样被调用一次。我尝试使用ng-init调用func,然后将其附加到作用域上的模型,但这也不起作用。
征服者

1
这是您在这个问题到处都可以看到的示例。如果Angular可以指向令人反感的功能,那么具有这种行为,那就太棒了……
Jorn 2015年

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 2015年

7

也有可能根本不是无限循环。10次​​迭代不足以得出肯定的结论。因此,在进行野鹅追逐之前,最好先排除这种可能性。

这样做的最简单方法是将最大摘要循环计数增加到更大的数量,这可以module.config使用$rootScopeProvider.digestTtl(limit)方法在该方法中完成。如果infdig错误不再出现,则您仅具有一些足够复杂的更新逻辑。

如果你建立依托循环的监视器中的数据或观点你可能想搜索迭代的解决方案使用(即不依赖于新的消化循环,以启动)whileforArray.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

我的项目中遇到了这个问题,因为.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">

0

这就是我采用的方法,并找到了解决方案:我检查了文本,结果显示:

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

观察者在最近5次迭代中触发了:[[{{msg“:” statement === statement && functionCall()“,” newVal“:[{” id“:7287,” referen ...

所以如果你能看到

味精

这就是产生错误的陈述。我检查了此消息中调用的函数,我从所有函数中返回(假)只是为了确定哪个有问题。其中之一是调用一个函数,该函数会不断更改收益,这就是问题所在。


-3

听起来很疯狂,我只是在突然出现时重新启动浏览器,从而解决了该错误。

因此,一种解决方案是仅清除浏览器的缓存或尝试重新启动浏览器。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.