Angularjs $ q ทั้งหมด


106

ฉันใช้ $ q.all ใน angularjs แล้ว แต่ฉันไม่สามารถทำให้โค้ดทำงานได้ นี่คือรหัสของฉัน:

UploadService.uploadQuestion = function(questions){

        var promises = [];

        for(var i = 0 ; i < questions.length ; i++){

            var deffered  = $q.defer();
            var question  = questions[i]; 

            $http({

                url   : 'upload/question',
                method: 'POST',
                data  : question
            }).
            success(function(data){
                deffered.resolve(data);
            }).
            error(function(error){
                deffered.reject();
            });

            promises.push(deffered.promise);
        }

        return $q.all(promises);
    }

และนี่คือตัวควบคุมของฉันที่เรียกใช้บริการ:

uploadService.uploadQuestion(questions).then(function(datas){

   //the datas can not be retrieved although the server has responded    
}, 
function(errors){ 
   //errors can not be retrieved also

})

ฉันคิดว่ามีปัญหาบางอย่างในการตั้งค่า $ q.all ในบริการของฉัน


1
คุณเห็นพฤติกรรมอะไร มันโทรมาที่คุณthen(datas)หรือไม่? ลองทำแค่pushนี้:promises.push(deffered);
Davin Tryon

@ themyth92 คุณลองวิธีแก้ปัญหาของฉันแล้วหรือยัง?
Ilan Frumer

ฉันได้ลองแล้วและทั้งสองวิธีใช้ได้ผลกับกรณีของฉัน แต่ฉันจะทำให้ @Llan Frumer เป็นคำตอบที่ถูกต้อง ขอบคุณทั้งสองท่านจริงๆ
themyth92

1
ทำไมคุณถึงทำตามสัญญาที่มีอยู่? $ http คืนสัญญาแล้ว การใช้ $ q.defer นั้นไม่จำเป็น
Pete Alvin

1
มันเป็นdeferredไม่ได้deffered:)
Christophe Roussy

คำตอบ:


225

ในจาวาสคริปต์ไม่มีblock-level scopesเพียงfunction-level scopes:

อ่านบทความนี้เกี่ยวกับjavaScript กำหนดขอบเขตและ Hoisting

ดูว่าฉันแก้ไขข้อบกพร่องรหัสของคุณอย่างไร:

var deferred = $q.defer();
deferred.count = i;

console.log(deferred.count); // 0,1,2,3,4,5 --< all deferred objects

// some code

.success(function(data){
   console.log(deferred.count); // 5,5,5,5,5,5 --< only the last deferred object
   deferred.resolve(data);
})
  • เมื่อคุณเขียนvar deferred= $q.defer();ภายใน for loop มันจะขึ้นไปที่ด้านบนสุดของฟังก์ชันนั่นหมายความว่า javascript จะประกาศตัวแปรนี้ในขอบเขตฟังก์ชันที่อยู่ภายนอกไฟล์for loop.
  • ในแต่ละลูปการเลื่อนครั้งสุดท้ายจะแทนที่การวนซ้ำก่อนหน้านี้ไม่มีขอบเขตระดับบล็อกที่จะบันทึกการอ้างอิงไปยังออบเจ็กต์นั้น
  • เมื่อมีการเรียกใช้การเรียกกลับแบบอะซิงโครนัส (ความสำเร็จ / ข้อผิดพลาด) จะอ้างอิงเฉพาะอ็อบเจ็กต์สุดท้ายที่รอการตัดบัญชีและได้รับการแก้ไขเท่านั้นดังนั้น$ q.all จึงไม่ได้รับการแก้ไขเนื่องจากยังคงรอออบเจ็กต์อื่นที่รอการตัดบัญชี
  • สิ่งที่คุณต้องการคือสร้างฟังก์ชันที่ไม่ระบุชื่อสำหรับแต่ละรายการที่คุณทำซ้ำ
  • เนื่องจากฟังก์ชันมีขอบเขตการอ้างอิงถึงอ็อบเจ็กต์ที่รอการตัดบัญชีจะถูกเก็บรักษาไว้closure scopeแม้จะมีการเรียกใช้ฟังก์ชันแล้วก็ตาม
  • ตามที่ #dfsq แสดงความคิดเห็น: ไม่จำเป็นต้องสร้างออบเจ็กต์ใหม่ที่รอการตัดบัญชีด้วยตนเองเนื่องจาก $ http ส่งคืนคำสัญญา

แก้ปัญหาด้วยangular.forEach:

นี่คือตัวอย่างสาธิต: http://plnkr.co/edit/NGMp4ycmaCqVOmgohN53?p=preview

UploadService.uploadQuestion = function(questions){

    var promises = [];

    angular.forEach(questions , function(question) {

        var promise = $http({
            url   : 'upload/question',
            method: 'POST',
            data  : question
        });

        promises.push(promise);

    });

    return $q.all(promises);
}

วิธีที่ฉันชอบคือใช้Array#map:

นี่คือเครื่องมือสาธิต: http://plnkr.co/edit/KYeTWUyxJR4mlU77svw9?p=preview

UploadService.uploadQuestion = function(questions){

    var promises = questions.map(function(question) {

        return $http({
            url   : 'upload/question',
            method: 'POST',
            data  : question
        });

    });

    return $q.all(promises);
}

14
คำตอบที่ดี. การเพิ่มเพียงครั้งเดียว: ไม่จำเป็นต้องสร้างใหม่รอการตัดบัญชีเนื่องจาก $ http ส่งคืนสัญญา ดังนั้นจึงสั้นลงได้: plnkr.co/edit/V3gh7Roez8WWl4NKKrqM?p=preview
dfsq

"เมื่อคุณเขียน var deferred = $ q.defer (); ข้างในสำหรับลูปมันจะขึ้นไปที่ด้านบนสุดของฟังก์ชัน" ฉันไม่เข้าใจส่วนนี้คุณช่วยอธิบายเหตุผลเบื้องหลังได้ไหม
themyth92

ฉันรู้ว่าฉันจะทำแบบเดียวกันจริงๆ
dfsq

4
รักการใช้mapเพื่อสร้างคำสัญญามากมาย เรียบง่ายและรัดกุม
Drumbeg

1
ควรสังเกตว่าการประกาศถูกยกขึ้น แต่การมอบหมายยังคงอยู่ในตำแหน่งนั้น นอกจากนี้ตอนนี้ยังมีการกำหนดขอบเขตระดับบล็อกด้วยคำสั่ง 'let' ดูdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Spencer

36

$ http เป็นสัญญาเช่นกันคุณสามารถทำให้ง่ายขึ้น:

return $q.all(tasks.map(function(d){
        return $http.post('upload/tasks',d).then(someProcessCallback, onErrorCallback);
    }));

2
ใช่คำตอบที่ได้รับการยอมรับเป็นเพียงการรวมตัวกันของรูปแบบการต่อต้าน
Roamer-1888

ฉันคิดว่าคุณสามารถออกจาก.then()ประโยคได้เนื่องจาก OP ต้องการทำทุกอย่างในคอนโทรลเลอร์ของเขา แต่หลักการนั้นถูกต้องทั้งหมด
Roamer-1888

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

1
@Zerkotin คุณสามารถthrowจาก.thenเพื่อที่จะจับทั้งสองได้ในภายหลังและเปิดเผยให้$exceptionHandlerซึ่งจะช่วยให้คุณประหยัดปัญหาและทั่วโลก
Benjamin Gruenbaum

ดี. โดยพื้นฐานแล้วนี่เป็นแนวทางเดียวกับวิธีการแก้ปัญหา / ตัวอย่างสุดท้ายของคำตอบที่ยอมรับ
Niko Bellic

12

ปัญหาน่าจะเป็นว่าคุณกำลังเพิ่มdeffered.promiseเมื่อdefferedเป็นตัวเองสัญญาที่คุณควรจะเพิ่ม:

ลองเปลี่ยนpromises.push(deffered);เพื่อที่คุณจะได้ไม่ต้องเพิ่มสัญญาที่ยังไม่ได้ใส่ลงในอาร์เรย์

 UploadService.uploadQuestion = function(questions){

            var promises = [];

            for(var i = 0 ; i < questions.length ; i++){

                var deffered  = $q.defer();
                var question  = questions[i]; 

                $http({

                    url   : 'upload/question',
                    method: 'POST',
                    data  : question
                }).
                success(function(data){
                    deffered.resolve(data);
                }).
                error(function(error){
                    deffered.reject();
                });

                promises.push(deffered);
            }

            return $q.all(promises);
        }

สิ่งนี้ส่งคืนเฉพาะอาร์เรย์ของวัตถุที่ถูกทำลายฉันตรวจสอบแล้ว
Ilan Frumer

ฉันไม่รู้ว่าเขาพูดอะไร แต่คอนโซลพูดว่าอะไรคุณจะเห็นว่ามันใช้งานไม่ได้: plnkr.co/edit/J1ErNncNsclf3aU86D7Z?p=preview
Ilan Frumer

4
นอกจากนี้เอกสารยังบอกอย่างชัดเจนว่า$q.allได้รับสัญญาไม่ใช่วัตถุรอการตัดบัญชี ปัญหาที่แท้จริงของ OP อยู่ที่การกำหนดขอบเขตและเนื่องจากมีเพียงการรอการตัดบัญชีครั้งสุดท้ายเท่านั้นที่ได้รับการแก้ไข
Ilan Frumer

Ilan ขอบคุณสำหรับการแยกdeferวัตถุและpromises . คุณแก้ไขall()ปัญหาของฉันเช่นกัน
Ross Rogers

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