【问题标题】:How to Troubleshoot Angular "10 $digest() iterations reached" Error如何解决 Angular “10 $digest() 迭代次数达到”错误
【发布时间】:2013-06-11 13:16:29
【问题描述】:

达到 10 次 $digest() 迭代。中止!

有很多支持文本,例如“在最后 5 次迭代中触发的观察者:”等,但其中很多文本是来自各种函数的 Javascript 代码。是否有诊断此问题的经验法则?这是一个始终可以缓解的问题,还是存在足够复杂的应用程序以致该问题应仅视为警告?

【问题讨论】:

    标签: angularjs


    【解决方案1】:

    正如 Ven 所说,您要么在每个 $digest 循环中返回不同(不相同)的对象,要么您更改数据的次数太多。

    找出应用程序的哪个部分导致此行为的最快解决方案是:

    1. 删除所有可疑的 HTML - 基本上从模板中删除所有 html,并检查是否没有警告
    2. 如果没有警告 - 添加您删除的 html 的一小部分并检查问题是否再次出现
    3. 重复第 2 步,直到收到警告 - 您将找出问题所在的 html 部分
    4. 进一步调查 - 步骤 3 中的部分负责改变 $scope 上的对象,或者在每个 $digest 循环中返回不同的对象。
    5. 如果您在第 1 步之后仍然有 $digest 迭代警告,那么您可能正在做一些非常可疑的事情。对父模板/范围/控制器重复相同的步骤

    您还需要确保没有更改自定义过滤器的输入

    请记住,在 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
    

    【讨论】:

    • 谢谢!这是一个有用的启发式方法。我还认为 Angular 为 ngRepeat 提供的新“track by”功能也会对我有所帮助。我在服务中使用 Underscore 做一些 map() 和 groupBy() 的东西,所以它肯定每次都返回“不同”的对象(即使它们在逻辑上代表相同的东西 - “track by Id”会帮助 Angular 看不到它们作为“改变”,如果他们没有真的)。
    • 如果您正在执行map()groupBy(),请确保您的$watches 通过objectEquality 执行脏检查$watch('myFunctionDoingGropby',callback,true) 参见docs.angularjs.org/api/ng.$rootScope.Scope#$watch
    • 根据上面的评论,“track by”解决了我的问题(请参阅此处的文档:docs.angularjs.org/api/ng/directive/ngRepeat)。我将不胜感激解释性评论解释为什么这样做......
    • @g00fy:这是一个很好的线索,我能够用我的 $scope 上的一个变量替换我的列表检索函数,该变量被分配一次 _.map-ed 列表 - 但在一般情况下,如果不是手动创建的$watch,而是ngRepeat,您将如何影响通过对象相等性进行的脏检查?
    • 这对我有用。有用的建议。禁用组件上的所有 HTML 控件,并一一启用它们以识别导致问题的原因。在阅读此答案之前,我不会考虑查看错误的 HTML 部分。我花了好几个小时盯着我的观察者和其他变量(徒劳)!
    【解决方案2】:

    当您每次返回不同的对象时,通常会发生这种情况。

    例如,如果您在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 has added some more in-depth explanation on their website

    【讨论】:

    • 如何阻止这种情况发生?我现在也有类似的情况。
    • 确保您没有在每次调用时创建不同的对象;)。
    • 我怎样才能确保这一点?我正在 func(obj) 中执行类似 item 的操作,但是 func 似乎被多次调用,而不是像我希望的那样被调用一次。我尝试使用 ng-init 调用 func,然后将其附加到作用域上的模型,但这也不起作用。
    • 这是你在这个问题上随处可见的例子。如果 Angular 可以指向具有这种行为的违规函数,那就太棒了……
    • @BenWheeler 自 2013 年以来它肯定有所改善,是的:P。
    【解决方案3】:

    只是想把这个解决方案放在这里,希望它能对其他人有所帮助。我遇到了这个迭代问题,因为我正在迭代生成的属性,每次调用它都会生成一个新对象。

    我通过在第一次请求生成的对象时缓存它来修复它,然后总是返回缓存(如果存在)。还添加了一个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 个小时的问题。我爱你!
    【解决方案4】:

    它也有可能根本不是无限循环。 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 来寻找问题的根源,当然是有帮助的。

    【讨论】:

      【解决方案5】:

      我遇到了同样的问题 - 我每次都在创建一个新日期。因此,对于任何处理日期的人,我都会像这样转换所有调用:

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

      到:

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

      初始化一个数字而不是一个日期对象为我解决了这个问题。

      【讨论】:

        【解决方案6】:

        简单的方法是: 使用 angular.js,而不是 min 文件。 打开它并找到该行:

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

        在下面添加一行:

        console.log("aaaa",watch)
        

        然后刷新您的页面,在开发工具控制台中,您将 找到你的错误代码。

        【讨论】:

          【解决方案7】:

          这是ui-router 中的一个已知错误,这对我们有帮助:https://github.com/angular-ui/ui-router/issues/600

          【讨论】:

          【解决方案8】:

          我还想提一下,当我在项目中的自定义指令的 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 错误。

          很容易被忽略,因为错误信息非常神秘,看起来与真正的问题无关。

          【讨论】:

            【解决方案9】:

            我在我的项目中遇到了这个问题,因为 .otherwise() 缺少我的路线定义并且我打错了路线。

            【讨论】:

              【解决方案10】:

              我遇到了这个问题,因为我正在这样做

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

              而不是这样:(注意 = vs ===),我的单元测试开始中断,我发现我的愚蠢

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

              【讨论】:

                【解决方案11】:

                我遇到了这个问题,我需要一个动态工具提示......它导致 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">
                

                【讨论】:

                  【解决方案12】:

                  这就是我接近它并找到解决方案的方式: 我检查了文字,它显示:

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

                  在最后 5 次迭代中触发的观察者:[[{"msg":"statement === statment && functionCall()","newVal":[{"id":7287,"referen...

                  所以如果你能看到

                  消息

                  这是产生错误的语句。我检查了这条消息中调用的函数,我从所有这些函数中返回(false)只是为了确定哪个有问题。 其中一个是调用一个不断改变返回值的函数,这就是问题所在。

                  【讨论】:

                    【解决方案13】:

                    听起来很疯狂,我只是通过在突然出现时重新启动浏览器来修复此错误。

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

                    【讨论】:

                      猜你喜欢
                      • 2015-05-30
                      • 2012-11-30
                      • 1970-01-01
                      • 2016-02-25
                      • 2015-05-07
                      • 2013-11-10
                      • 1970-01-01
                      • 2015-11-11
                      • 1970-01-01
                      相关资源
                      最近更新 更多