$ ใช้แล้วเกิดข้อผิดพลาดในการดำเนินการ


134

ติดตามกอง:

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

อ้างถึงรหัสนี้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
        });
    })

สิ่งที่แปลกใน LG4X ของฉันมันใช้งานได้ดี แต่บน samsung s2 ของฉันมันแสดงข้อผิดพลาดข้างต้น ความคิดใดผิดพลาด?


1
คุณลองstackoverflow.com/a/12859093/1266600หรือยัง อาจเป็นเพราะอุปกรณ์ต่างกัน -> ความเร็วในการประมวลผลต่างกัน -> การกำหนดเวลาที่แตกต่างกันซึ่งอาจทำให้เกิดความขัดแย้งในบางแห่ง
sushain97

20
ใช้$timeout()
Onur Yıldırım

7
+1 ถึง $ timeout () ความคิดเห็น ดู: stackoverflow.com/questions/12729122/…
Trevor

คำตอบ:


106

คุณได้รับข้อผิดพลาดนี้เนื่องจากคุณกำลังเรียกใช้$applyในวงจรการย่อยอาหารที่มีอยู่

คำถามใหญ่คือคุณโทรมา$applyทำไม? คุณไม่จำเป็นต้องโทร$applyเว้นแต่ว่าคุณกำลังเชื่อมต่อจากเหตุการณ์ที่ไม่ใช่เชิงมุม การมีอยู่ของ$applyโดยปกติหมายความว่าฉันทำอะไรผิดพลาด (เว้นแต่อีกครั้งการใช้ $ ใช้เกิดขึ้นจากเหตุการณ์ที่ไม่ใช่เชิงมุม)

หากตรง$applyนี้เหมาะสมจริงๆให้พิจารณาใช้แนวทาง "ใช้อย่างปลอดภัย":

https://coderwall.com/p/ngisma


41
หลักของความปลอดภัยในการเชื่อมโยงนำไปใช้เป็นรูปแบบการป้องกัน (ตามเอกสาร) github.com/angular/angular.js/wiki/Anti-Patterns หากคุณต้องการวิธีดำเนินการที่รองรับในอนาคต ($$ เฟสกำลังจะหายไป!) ให้ห่อรหัสของคุณเป็น $ timeout () โดยไม่ต้องกำหนดเวลา จะใช้ได้อย่างปลอดภัยหลังจากรอบการย่อยปัจจุบันเสร็จสิ้น
betaorbust

@betaorbust เห็นด้วย ใช้อย่างปลอดภัยไม่ดี นอกจากนี้การโทรใช้หลายครั้งเกินไปอาจทำให้เกิดปัญหาเกี่ยวกับความสมบูรณ์แบบ ที่ดีที่สุดคือจัดโครงสร้างโค้ดเพื่อหลีกเลี่ยงปัญหาทั้งหมดเข้าด้วยกัน
Brian Genisio

im ไม่เรียกใช้
วงจร


41

คุณสามารถใช้คำสั่งนี้:

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

1
ไม่แนะนำให้ใช้ตัวแปรที่ขึ้นต้นด้วย $$ เนื่องจากเป็นตัวแปรส่วนตัว ในกรณีนี้ $$ เฟส
Ara Yeressian

10
คำตอบนี้มีประโยชน์มากกว่าคำตอบข้างต้น ฉันต้องการวิธีแก้ไขไม่ต้องถูกตักเตือนสำหรับบางสิ่งที่อาจอยู่เหนือการควบคุมของฉัน เรามีรหัสเชิงมุมและรหัสเดิมผสมกันและต้องมีการโต้ตอบกัน แพงเกินไปที่จะเขียนรหัสเดิมทั้งหมดใหม่ ...
Jordan Lapp

24

หากต้องใช้ขอบเขตในบางกรณีคุณสามารถกำหนดระยะหมดเวลาเพื่อให้ $ ใช้เลื่อนออกไปจนกว่าจะถึงขีดถัดไป

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

หรือห่อรหัสของคุณเป็น $ timeout (function () {.. }); เนื่องจากจะ $ ใช้ขอบเขตโดยอัตโนมัติเมื่อสิ้นสุดการดำเนินการ หากคุณต้องการให้ฟังก์ชันของคุณทำงานพร้อมกันฉันจะทำก่อน


ฉันพบว่าฉันต้องการรวมการดำเนินการไว้ในsetTimeout(function() { $apply(function() {... do stuff ...} ) })@Tamil Vendhan ด้านล่าง
ต้นแบบ

6
อย่าใช้ setTimeout เพียงแค่สร้างความจำเป็นในการสมัคร $ อื่น ใช้กรอบมันมีบริการ $ timeout ที่ทำทุกอย่างให้คุณ
Spencer

10

ในกรณีของฉันฉันใช้$applyกับ UI ปฏิทินเชิงมุมเพื่อเชื่อมโยงเหตุการณ์บางอย่าง:

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

หลังจากอ่านเอกสารของปัญหา: https://docs.angularjs.org/error/ $ rootScope / inprog

ส่วนที่ไม่สอดคล้องกันAPI (Sync / Async)นั้นน่าสนใจมาก:

ตัวอย่างเช่นลองนึกภาพไลบรารีของบุคคลที่สามที่มีวิธีการที่จะดึงข้อมูลมาให้เรา เนื่องจากอาจทำการโทรแบบอะซิงโครนัสไปยังเซิร์ฟเวอร์จึงยอมรับฟังก์ชันเรียกกลับซึ่งจะถูกเรียกเมื่อข้อมูลมาถึง

เนื่องจากตัวสร้าง MyController จะถูกสร้างอินสแตนซ์จากภายในการเรียกใช้ $ ใช้เสมอตัวจัดการของเราจึงพยายามป้อนบล็อก $ ใช้ใหม่จากภายในหนึ่ง

ฉันเปลี่ยนรหัสเป็น:

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

ใช้งานได้เหมือนมีเสน่ห์!

ที่นี่เราใช้ $ timeout เพื่อกำหนดการเปลี่ยนแปลงขอบเขตใน call stack ในอนาคต ด้วยการระบุช่วงหมดเวลาเป็น 0 มิลลิวินาทีสิ่งนี้จะเกิดขึ้นโดยเร็วที่สุดและ $ timeout จะทำให้แน่ใจว่ารหัสจะถูกเรียกในบล็อก $ ใช้เดียว


1
โซลูชัน $ timeout delay 0 ของคุณยอดเยี่ยมมาก
Ahsan

9

ในเชิงมุม 1.3 ผมคิดว่าพวกเขาเพิ่มฟังก์ชั่นใหม่ $scope.$applyAsync()- การเรียกใช้ฟังก์ชันนี้จะมีผลในภายหลัง - อย่างน้อยประมาณ 10 ms ในภายหลัง มันไม่สมบูรณ์แบบ แต่อย่างน้อยก็กำจัดข้อผิดพลาดที่น่ารำคาญ

https://docs.angularjs.org/api/ng/type/ $ rootScope.Scope # $ applyAsync


3

ณ เวลาใดเวลาหนึ่งอาจมีเพียงหนึ่งรายการ$digestหรือกำลัง$applyดำเนินการอยู่ นี่เป็นการป้องกันไม่ให้ตรวจพบข้อบกพร่องเข้าสู่แอปพลิเคชันของคุณได้ยากมาก การติดตามสแต็กของข้อผิดพลาดนี้ช่วยให้คุณสามารถติดตามที่มาของการเรียกใช้$applyหรือการ$digestเรียกใช้ในปัจจุบันซึ่งทำให้เกิดข้อผิดพลาด

ข้อมูลเพิ่มเติม: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply


2

เพิ่งแก้ไขปัญหานี้ เอกสารของที่นี่

ฉันโทรไป$rootScope.$applyสองครั้งในขั้นตอนเดียวกัน ทั้งหมดที่ฉันทำคือรวมเนื้อหาของฟังก์ชันบริการด้วยไฟล์setTimeout(func, 1).


1

ฉันรู้ว่ามันเป็นคำถามเก่า แต่ถ้าคุณต้องการใช้ $ scope จริงๆ $ applyAsync ();


0

ฉันเรียก $ ขอบเขต $ ใช้แบบนี้กับการโทรที่ถูกละเว้นหลายครั้งในครั้งเดียว

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

โทร

callApply();

0

เราสามารถใช้setTimeoutฟังก์ชันในกรณีเช่นนี้

console.log('primary task');

setTimeout(function() {
  
  console.log('secondary task');

}, 0);

เพื่อให้แน่ใจว่างานรองจะถูกดำเนินการเมื่อการดำเนินการของงานหลักเสร็จสิ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.