Cách khắc phục lỗi Angular "Đã đạt đến số lần lặp 10 $ tiêu hóa ()"


91

Đã đạt đến 10 lần lặp lại tiêu hóa $ (). Hủy bỏ!

Có rất nhiều văn bản hỗ trợ theo nghĩa "Người theo dõi đã kích hoạt trong 5 lần lặp lại gần nhất:", v.v., nhưng rất nhiều văn bản này là mã Javascript từ các hàm khác nhau. Có quy tắc ngón tay cái để chẩn đoán vấn đề này không? Đó có phải là một vấn đề LUÔN CÓ THỂ được giảm nhẹ hay có các ứng dụng đủ phức tạp để vấn đề này chỉ nên được coi là một cảnh báo?

Câu trả lời:


80

như Ven đã nói, bạn đang trả về các đối tượng khác nhau (không giống hệt nhau) trên mỗi $digestchu kỳ, hoặc bạn đang thay đổi dữ liệu quá nhiều lần.

Giải pháp nhanh nhất để tìm ra phần nào trong ứng dụng của bạn đang gây ra hành vi này là:

  1. xóa tất cả HTML đáng ngờ - về cơ bản xóa tất cả html của bạn khỏi mẫu và kiểm tra xem có cảnh báo nào không
  2. nếu không có cảnh báo - hãy thêm các phần nhỏ của html bạn đã xóa và kiểm tra xem sự cố có trở lại không
  3. lặp lại bước 2 cho đến khi bạn nhận được cảnh báo - bạn sẽ tìm ra phần nào trong html của bạn chịu trách nhiệm cho sự cố
  4. điều tra thêm - phần từ bước 3 chịu trách nhiệm về việc thay đổi các đối tượng trên $scopehoặc trả về các đối tượng không giống nhau trên mỗi $digestchu kỳ.
  5. nếu bạn vẫn có $digestcảnh báo lặp lại sau bước 1, thì có lẽ bạn đang làm điều gì đó rất đáng ngờ. Lặp lại các bước tương tự cho mẫu / phạm vi / bộ điều khiển gốc

Bạn cũng muốn đảm bảo rằng bạn không thay đổi đầu vào của bộ lọc tùy chỉnh của mình

Hãy nhớ rằng trong JavaScript có các loại đối tượng cụ thể không hoạt động như bạn thường mong đợi:

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
Cảm ơn bạn! Đây là một kinh nghiệm hữu ích. Tôi cũng nghĩ rằng tính năng "theo dõi theo" mới của Angular cho ngRepeat cũng sẽ giúp tôi. Tôi đang thực hiện một số nội dung map () và groupBy () bằng cách sử dụng Dấu gạch dưới trong một dịch vụ, vì vậy nó chắc chắn trả về các đối tượng "khác nhau" mỗi lần (mặc dù chúng đại diện một cách hợp lý cho những thứ giống nhau - "theo dõi bằng Id" sẽ giúp Angular không nhìn thấy chúng như một "thay đổi" nếu họ chưa thực sự).
blaster

2
Nếu bạn đang làm map()groupBy()hơn làm shure rằng bạn $watches thực hiện kiểm tra bẩn bởi objectEquality $watch('myFunctionDoingGropby',callback,true) see docs.angularjs.org/api/ng.$rootScope.Scope#$watch
g00fy

Theo nhận xét ở trên, "theo dõi bằng" đã khắc phục sự cố của tôi (xem tài liệu tại đây: docs.angularjs.org/api/ng/directive/ngRepeat ). Tôi sẽ biết ơn cho một lời chú thích giải thích tại sao các công trình này ...
Soferio

@ g00fy: Đó là một khách hàng tiềm năng và tôi đã có thể thay thế hàm truy xuất danh sách của mình bằng một biến trên $scopeđó được gán _.mapdanh sách -ed một lần - nhưng trong trường hợp chung, bạn sẽ ảnh hưởng như thế nào đến việc kiểm tra bẩn bằng đối tượng bình đẳng nếu nó không phải được tạo thủ công $watchđang vấp phải, nhưng ngRepeat?
HOẶC Người lập bản đồ

73

Thông thường điều đó xảy ra khi bạn trả lại một đối tượng khác nhau mỗi lần.

Ví dụ: nếu bạn sử dụng điều này trong ng-repeat:

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

Bạn sẽ nhận được thông báo lỗi này vì Angular cố gắng có "độ ổn định" và sẽ thực thi hàm cho đến khi nó trả về cùng một kết quả 2 lần (so sánh với ===), trong trường hợp của chúng tôi sẽ không bao giờ trả về true vì hàm luôn trả về một đối tượng mới.

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

Trong trường hợp này, bạn có thể khắc phục bằng cách lưu trữ đối tượng trong phạm vi trực tiếp, ví dụ:

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

Bằng cách đó, bạn luôn trả lại cùng một đối tượng!

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

(Bạn sẽ không bao giờ gặp phải điều đó, ngay cả trên các ứng dụng phức tạp).

Cập nhật: Angular đã thêm một số giải thích chuyên sâu hơn trên trang web của họ .


Làm thế nào để bạn ngăn chặn điều này xảy ra? Tôi đang gặp một tình huống tương tự ngay bây giờ.
Conqueror

2
Đảm bảo rằng bạn không tạo các đối tượng khác nhau trong mỗi cuộc gọi;).
Ven

Làm thế nào tôi có thể đảm bảo điều này? Tôi đang làm một cái gì đó giống như mục trong func (obj), nhưng func dường như được gọi nhiều lần thay vì một lần như tôi hy vọng. Tôi đã cố gắng sử dụng ng-init để gọi func và sau đó gắn nó vào một mô hình trên phạm vi nhưng điều đó cũng không hoạt động.
Conqueror

1
Đây là ví dụ bạn thấy ở khắp mọi nơi về vấn đề này. Sẽ thật tuyệt vời nếu Angular có thể trỏ đến chức năng vi phạm, có hành vi này ...
Jorn

1
@BenWheeler Chắc chắn nó đã được cải thiện kể từ năm 2013, vâng: P.
Ven

11

Tôi chỉ muốn đưa giải pháp này vào đây, hy vọng nó sẽ giúp ích cho những người khác. Tôi gặp sự cố lặp lại này vì tôi đang lặp lại một thuộc tính đã tạo đang tạo một đối tượng mới mỗi khi nó được gọi.

Tôi đã sửa nó bằng cách lưu vào bộ nhớ đệm đối tượng được tạo lần đầu tiên nó được yêu cầu và sau đó luôn trả về bộ đệm nếu nó tồn tại. Một phương thức dirty () cũng được thêm vào, phương thức này sẽ hủy kết quả được lưu trong bộ nhớ cache khi cần thiết.

Tôi đã có một cái gì đó như thế này:

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

            return retObj;
        }
    });
}

Và đây là giải pháp được triển khai:

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

            return _cached;
        }
    });

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

Ôi trời, tôi ước gì tôi có thể cho bạn 10 phiếu ủng hộ. Bạn vừa giải quyết một vấn đề khiến tôi đập đầu vào tường trong gần 3 giờ. Tôi mến bạn!
GMA

7

Cũng có khả năng nó không phải là một vòng lặp vô hạn. 10 lần lặp không phải là một con số đủ lớn để kết luận điều đó một cách chắc chắn. Vì vậy, trước khi thực hiện một cuộc rượt đuổi ngỗng trời, có thể nên loại trừ khả năng đó trước.

Phương pháp dễ nhất để làm như vậy là tăng số vòng lặp thông báo tối đa lên một số lớn hơn nhiều, có thể được thực hiện trong module.configphương thức, sử dụng $rootScopeProvider.digestTtl(limit)phương thức. Nếu infdiglỗi không còn xuất hiện, bạn chỉ cần có một số logic cập nhật đủ phức tạp.

Nếu bạn xây dựng dữ liệu hoặc quan điểm dựa vào đồng hồ đệ quy bạn có thể muốn tìm kiếm giải pháp lặp đi lặp lại (tức là không dựa vào mới tiêu hóa vòng được bắt đầu) sử dụng while, forhoặcArray.forEach . Đôi khi cấu trúc chỉ được lồng ghép cao và thậm chí không có tính đệ quy, có lẽ không có nhiều việc phải làm trong những trường hợp đó ngoại trừ việc nâng cao giới hạn.

Một phương pháp gỡ lỗi khác là xem xét dữ liệu thông báo. Nếu bạn in JSON đẹp, bạn sẽ nhận được một mảng các mảng. Mỗi mục nhập cấp cao nhất đại diện cho một lần lặp, mỗi lần lặp lại bao gồm một danh sách các mục theo dõi.

Ví dụ, nếu bạn có một thuộc tính được sửa đổi trong $watchchính nó, bạn có thể dễ dàng thấy rằng giá trị đang thay đổi vô hạn:

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

Tất nhiên trong dự án lớn hơn, điều này có thể không đơn giản, đặc biệt là vì msgtrường thường có giá trị "fn: regularInterceptedExpression"nếu đồng hồ là{{ }} phép nội suy.

Ngoài ra, các phương pháp đã được đề cập, như cắt bớt HTML để tìm ra nguồn gốc của vấn đề, tất nhiên là hữu ích.


6

Tôi cũng gặp phải vấn đề tương tự - tôi luôn tạo một ngày mới. Vì vậy, đối với bất kỳ ai xử lý ngày tháng, tôi đã chuyển đổi tất cả các cuộc gọi như thế này:

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

đến:

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

Việc khởi tạo một số thay vì một đối tượng ngày đã giải quyết được vấn đề đó cho tôi.


2

cách dễ dàng là: sử dụng angle.js, không phải tệp min. mở nó và tìm dòng:

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

thêm dòng bên dưới:

console.log("aaaa",watch)

và sau đó làm mới trang của bạn, trong bảng điều khiển công cụ dành cho nhà phát triển, bạn sẽ tìm thấy mã lỗi.



0

Tôi cũng muốn đề cập rằng tôi đã nhận được thông báo lỗi này khi tôi có lỗi đánh máy trong templateUrl của một chỉ thị tùy chỉnh mà tôi có trong dự án của mình. Do lỗi đánh máy, không thể tải mẫu.

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

Tìm trong tab mạng của các công cụ dành cho nhà phát triển của trình duyệt web của bạn và xem có tài nguyên nào đang gặp lỗi 404 hay không.

Dễ dàng bỏ qua, vì thông báo lỗi rất khó hiểu và dường như không liên quan đến vấn đề thực sự.


0

Tôi đang gặp sự cố này trong dự án của mình vì .otherwise () thiếu định nghĩa tuyến đường của tôi và tôi đã đánh sai tuyến.


0

Tôi gặp sự cố này vì tôi đang làm việc này

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

Thay vì điều này: (thông báo = so với ===), bài kiểm tra đơn vị của tôi bắt đầu bị phá vỡ và tôi nhận thấy sự ngu ngốc của mình

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

0

Tôi gặp phải vấn đề này khi tôi cần một chú giải công cụ động ... nó khiến cho góc phải tính toán lại nó mỗi lần như một giá trị mới (mặc dù nó giống nhau). Tôi đã tạo một hàm để lưu vào bộ nhớ cache giá trị được tính toán như sau:

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

Sau đó, trong html, tôi truy cập nó như thế này:

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

0

đây là cách tôi tiếp cận nó và tìm ra giải pháp: Tôi đã kiểm tra văn bản, nó cho thấy:

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

Người theo dõi đã kích hoạt trong 5 lần lặp lại gần đây nhất: [[{"msg": "statement === statment && functionCall ()", "newVal": [{"id": 7287, "reference ...

vì vậy nếu bạn có thể thấy

tin nhắn

đó là trạng thái tạo ra lỗi. Tôi đã kiểm tra hàm được gọi trong thông báo này, tôi trả về (false) từ tất cả chúng chỉ để xác định hàm nào có vấn đề. một trong số họ đang gọi một hàm liên tục thay đổi kết quả trả về, đó là vấn đề.


-3

Nghe có vẻ điên rồ, tôi đã sửa lỗi này chỉ bằng cách khởi động lại trình duyệt của mình khi nó đột ngột xuất hiện.

Vì vậy, một giải pháp là chỉ cần xóa bộ nhớ cache của trình duyệt hoặc thử khởi động lại trình duyệt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.