2013-09-05 21 views
114

Stack trace:

Error: $apply already in progress 
at Error (<anonymous>) 
at beginPhase (file:///android_asset/www/built.min.js:7:22740) 
at Object.Scope.$apply (file:///android_asset/www/built.min.js:7:25967) 
at navigator.geolocation.getCurrentPosition.that (file:///android_asset/www/built.min.js:13:8670) 
at Object.geolocation.getCurrentPosition (file:///android_asset/www/plugins/org.apache.cordova.core.geolocation/www/geolocation.js:122:13) 
at Object.getCurrentPosition (file:///android_asset/www/built.min.js:13:8589) 
at Object.getCurrentPosition (file:///android_asset/www/built.min.js:13:8277) 
at Object.getCurrentCity (file:///android_asset/www/built.min.js:13:8941) 
at Object.$scope.locateDevice (file:///android_asset/www/built.min.js:13:10480) 
at file:///android_asset/www/built.min.js:7:12292:7 

đề cập đến mã này http://pastebin.com/B9V6yvFu

getCurrentPosition: cordovaReady(function (onSuccess, onError, options) { 

     navigator.geolocation.getCurrentPosition(function() { 
      var that = this, 
       args = arguments; 

      if (onSuccess) { 
       $rootScope.$apply(function() { 
        onSuccess.apply(that, args); 
       }); 
      } 
     }, function() { 
      var that = this, 
       args = arguments; 
      if (onError) { 
       $rootScope.$apply(function() { 
        onError.apply(that, args); 
       }); 
      } 
     }, { 
      enableHighAccuracy: true, 
      timeout: 20000, 
      maximumAge: 18000000 
     }); 
    }) 

Lạ một điều, trên tôi LG4X nó hoạt động tốt, tuy nhiên trên samsung của tôi s2 nó ném ở trên lỗi. Bất kỳ ý tưởng whats sai?

+1

Bạn đã thử http://stackoverflow.com/a/12859093/1266600 chưa? Nó có thể là do các thiết bị khác nhau -> tốc độ xử lý khác nhau -> thời gian khác nhau, có thể gây xung đột ở một số nơi nhưng không gây ra xung đột khác. – sushain97

+17

sử dụng '$ timeout()' –

+6

+1 vào nhận xét $ timeout(). Xem: http://stackoverflow.com/questions/12729122/prevent-error-digest-already-in-progress-when-calling-scope-apply?answertab=active#21269546 – Trevor

Trả lời

93

Bạn nhận được lỗi này vì bạn đang gọi $apply bên trong chu kỳ tiêu hóa hiện có.

Câu hỏi lớn đặt ra là: tại sao bạn gọi số $apply? Bạn không bao giờ cần phải gọi $apply trừ khi bạn đang giao tiếp từ một sự kiện không phải là Góc. Sự tồn tại của $apply thường có nghĩa là tôi đang làm điều gì đó sai (trừ khi, một lần nữa, $ áp dụng xảy ra từ một sự kiện không Góc).

Nếu $apply thực sự là thích hợp ở đây, hãy xem xét sử dụng một "an toàn áp dụng" phương pháp tiếp cận:

https://coderwall.com/p/ngisma

+36

Cốt lõi của an toàn liên kết được áp dụng là chống -pattern (theo tài liệu) http://github.com/angular/angular.js/wiki/Anti-Patterns. Nếu bạn muốn một tương lai được hỗ trợ ($$ giai đoạn là đi xa!) Cách để làm điều đó, quấn mã của bạn trong một $ timeout() mà không có thời gian thiết lập. Nó sẽ áp dụng một cách an toàn sau khi chu kỳ tiêu hóa hiện tại đã hoàn thành. – betaorbust

+0

@betaorbust Đồng ý. Áp dụng an toàn là xấu. Ngoài ra, gọi điện áp dụng quá nhiều lần có thể gây ra vấn đề về sự hoàn hảo. Tốt nhất là cấu trúc mã để tránh vấn đề tất cả cùng nhau. –

+0

im không gọi áp dụng – circuitry

22

Nếu phạm vi phải được áp dụng trong một số trường hợp, sau đó bạn có thể thiết lập một thời gian chờ để các $ áp dụng là được hoãn cho đến lần đánh dấu tiếp theo

setTimeout(function(){ scope.$apply(); }); 

hoặc quấn mã của bạn trong $ timeout (function() {..}); bởi vì nó sẽ tự động $ áp dụng phạm vi ở cuối thực thi. Nếu bạn cần chức năng của bạn để hành xử đồng bộ, tôi sẽ làm điều đầu tiên.

+0

Tôi thấy tôi cần bao gồm hành động trong 'setTimeout (function() {$ apply (hàm() {... do stuff ...})})' trên @Tamil Vendhan bên dưới. – prototype

+6

Không sử dụng setTimeout, mà chỉ tạo ra sự cần thiết cho một $ áp dụng. Sử dụng khung, nó có một dịch vụ timeout $ mà làm tất cả những gì cho bạn. – Spencer

+0

Ít nhất sử dụng '$ timeout' để mã có thể được kiểm tra. –

38

Bạn có thể sử dụng câu lệnh:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') { 
    $scope.$apply(); 
} 
+1

Không nên sử dụng các biến bắt đầu bằng $$ vì chúng là riêng tư. Trong trường hợp này là $$ phase –

+8

Câu trả lời này hữu ích hơn nhiều so với câu trả lời ở trên. Tôi cần một giải pháp, không được khuyên nhủ vì điều gì đó có thể vượt khỏi tầm kiểm soát của tôi.Chúng tôi có một hỗn hợp của mã góc và di sản, và họ phải tương tác bằng cách nào đó. Nó quá đắt để chỉ viết lại tất cả các mã cũ ... –

2

Chỉ cần giải quyết vấn đề này. Tài liệu của nó là here.

Tôi đã gọi số $rootScope.$apply hai lần trong cùng một luồng. Tất cả những gì tôi đã làm là gói nội dung của chức năng dịch vụ với setTimeout(func, 1).

+1

@ downvoter: Tôi có thể biết tại sao không? –

3

Tại bất kỳ thời điểm nào, chỉ có thể có một hoạt động $digest hoặc $apply đang tiến hành. Điều này là để ngăn chặn rất khó để phát hiện lỗi xâm nhập vào ứng dụng của bạn. Theo dõi ngăn xếp của lỗi này cho phép bạn theo dõi nguồn gốc của cuộc gọi hiện tại đang thực hiện $apply hoặc $digest gây ra lỗi.

Thông tin thêm: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

54

Chỉ cần sử dụng $evalAsync thay vì $apply.

+9

Bài viết giải thích về evalAsync tại http://www.panda-os.com/2015/01/angularjs-apply-digest-and-evalasync/#.VVOClflVhBd –

+0

Hoàn hảo - cảm ơn! – Dallas

8

Trong góc 1.3, tôi nghĩ, họ đã thêm một chức năng mới - $scope.$applyAsync(). Chức năng này gọi áp dụng sau này - họ nói khoảng 10 ms sau đó ít nhất. Nó không phải là hoàn hảo, nhưng nó ít nhất là loại bỏ các lỗi gây phiền nhiễu.

https://docs.angularjs.org/api/ng/type/ $ rootScope.Phạm vi # $ applyAsync

7

Trong trường hợp của tôi tôi sử dụng $apply với UI lịch góc liên kết một số sự kiện:

$scope.eventClick = function(event){   
    $scope.$apply(function() { 
     $location.path('/event/' + event.id); 
    }); 
}; 

Sau khi đọc doc của vấn đề: https://docs.angularjs.org/error/ $ rootScope/inprog

Phần không phù hợp API (Đồng bộ/Async) rất thú vị:

Ví dụ: hãy tưởng tượng thư viện của bên thứ ba có một phương pháp sẽ lấy dữ liệu cho chúng tôi. Vì nó có thể thực hiện một cuộc gọi không đồng bộ đến một máy chủ, nó chấp nhận một hàm gọi lại, nó sẽ được gọi khi dữ liệu đến.

Vì, hàm tạo MyController luôn được khởi tạo từ bên trong một lệnh $ apply, trình xử lý của chúng tôi đang cố gắng nhập một khối $ áp dụng mới từ bên trong một.

tôi thay đổi mã để:

$scope.eventClick = function(event){   
    $timeout(function() { 
     $location.path('/event/' + event.id); 
    }, 0); 
}; 

trình như một say mê!

Ở đây chúng tôi đã sử dụng $ timeout để lên lịch các thay đổi cho phạm vi trong ngăn xếp cuộc gọi trong tương lai. Bằng cách cung cấp khoảng thời gian chờ 0ms, điều này sẽ xảy ra càng sớm càng tốt và $ timeout sẽ đảm bảo rằng mã sẽ được gọi trong một khối $ áp dụng duy nhất.

0

Tôi gọi $ scope. $ Áp dụng như thế này để bỏ qua cuộc gọi nhiều lần trong một lần.

 var callApplyTimeout = null; 
     function callApply(callback) { 
      if (!callback) callback = function() { }; 
      if (callApplyTimeout) $timeout.cancel(callApplyTimeout); 

      callApplyTimeout = $timeout(function() { 
       callback(); 
       $scope.$apply(); 
       var d = new Date(); 
       var m = d.getMilliseconds(); 
       console.log('$scope.$apply(); call ' + d.toString() + ' ' + m); 
      }, 300); 
     } 

chỉ cần gọi

callApply(); 
0

Tôi biết đó là câu hỏi cũ nhưng nếu bạn thực sự cần sử dụng $ phạm vi $ applyAsync().