Angularjs $ q.all


106

Saya telah menerapkan $ q.all di angularjs, tetapi saya tidak dapat membuat kode berfungsi. Ini kode saya:

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

Dan inilah pengontrol saya yang memanggil layanan:

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

})

Saya rasa ada masalah saat menyiapkan $ q.all di layanan saya.


1
Perilaku apa yang Anda lihat? Apakah itu memanggil Anda then(datas)? Coba lakukan pushini:promises.push(deffered);
— Davin Tryon

@ themyth92 sudahkah Anda mencoba solusi saya?
— Ilan Frumer

Saya telah mencoba dan kedua metode berfungsi pada kasus saya. Tapi saya akan menjadikan @Llan Frumer sebagai jawaban yang benar. Benar-benar terima kasih kalian berdua.
— themyth92

1
Mengapa Anda menjanjikan janji yang ada? $ http sudah mengembalikan janji. Penggunaan $ q.defer tidak berguna.
— Pete Alvin

1
Hal ini deferredtidak deffered:)
— Christophe Roussy

Jawaban:


225

Di javascript tidak block-level scopeshanya ada function-level scopes:

Baca artikel ini tentang Scoping and Hoisting javaScript .

Lihat bagaimana saya men-debug kode Anda:

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);
})
  • Ketika Anda menulis var deferred= $q.defer();di dalam perulangan for itu diangkat ke atas fungsi, itu berarti bahwa javascript mendeklarasikan variabel ini pada ruang lingkup fungsi di luar for loop.
  • Dengan setiap loop, yang terakhir penangguhan menggantikan yang sebelumnya, tidak ada cakupan tingkat blok untuk menyimpan referensi ke objek itu.
  • Saat callback asinkron (berhasil / error) dipanggil, callback hanya merujuk objek yang ditangguhkan terakhir dan hanya itu yang diselesaikan, jadi $ q.all tidak pernah diselesaikan karena masih menunggu objek lain yang ditangguhkan.
  • Yang Anda butuhkan adalah membuat fungsi anonim untuk setiap item yang Anda iterasi.
  • Karena fungsi memang memiliki cakupan, referensi ke objek yang ditangguhkan dipertahankan di a closure scope bahkan setelah fungsi dijalankan.
  • Seperti komentar #dfsq: Tidak perlu membuat objek baru yang ditangguhkan secara manual karena $ http itu sendiri mengembalikan sebuah promise.

Solusi dengan angular.forEach :

Berikut ini demo plunker: 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);
}

Cara favorit saya adalah menggunakan Array#map :

Berikut ini demo plunker: 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
Jawaban yang bagus. Satu tambahan: tidak perlu membuat deferred baru karena $ http itu sendiri mengembalikan promise. Jadi bisa lebih pendek: plnkr.co/edit/V3gh7Roez8WWl4NKKrqM?p=preview
— dfsq

"Saat Anda menulis var deferred = $ q.defer (); di dalam perulangan for, itu akan diangkat ke atas fungsi.". Saya tidak mengerti bagian ini, bisakah Anda menjelaskan alasan di baliknya?
— themyth92

Saya tahu, saya akan melakukan hal yang sama.
— dfsq

4
Senang menggunakan mapuntuk membangun serangkaian janji. Sangat sederhana dan ringkas.
— Drumbeg

1
Perlu dicatat bahwa deklarasi itu diangkat, tetapi tugas tetap di tempatnya. Juga, sekarang ada pelingkupan level blok dengan pernyataan 'let'. Lihat developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
— Spencer

36

$ http juga merupakan janji, Anda dapat membuatnya lebih sederhana:

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

2
Ya, jawaban yang diterima hanyalah aglomerasi anti-pola.
— Roamer-1888

Saya pikir Anda dapat meninggalkan .then()klausul karena OP ingin melakukan semua itu di pengontrolnya, tetapi prinsipnya sepenuhnya benar.
— Roamer-1888

2
tentu saja Anda dapat menghilangkan klausa then, saya menambahkannya jika Anda ingin mencatat semua permintaan HTTP, saya selalu menambahkannya dan menggunakan callback onError global, untuk menangani semua pengecualian server.
— Zerkotin

1
@ Zerkotin Anda dapat throwdari .thenuntuk menanganinya nanti dan memaparkannya $exceptionHandler, yang akan menyelamatkan Anda dari masalah itu dan global.
— Benjamin Gruenbaum

Bagus. Ini pada dasarnya adalah pendekatan yang sama dengan solusi / contoh terakhir dari jawaban yang diterima.
— Niko Bellic

12

Masalahnya tampaknya Anda menambahkan deffered.promisekapan deffereditu sendiri janji yang harus Anda tambahkan:

Coba ubah menjadi promises.push(deffered);agar Anda tidak menambahkan janji yang belum dibungkus ke array.

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

Ini hanya mengembalikan array objek yang ditangguhkan, saya memeriksanya.
— Ilan Frumer

Saya tidak tahu apa yang dia katakan, hanya apa yang dikatakan konsol, Anda dapat melihat itu tidak berfungsi: plnkr.co/edit/J1ErNncNsclf3aU86D7Z?p=preview
— Ilan Frumer

4
Juga dokumentasinya mengatakan dengan jelas bahwa $q.allmendapat janji bukan objek yang ditangguhkan. Masalah sebenarnya dari OP adalah dengan pelingkupan dan karena hanya penundaan terakhir yang diselesaikan
— Ilan Frumer

Ilan, terima kasih telah menguraikan deferobjek dan promises. Anda memperbaiki all()masalah saya juga.
— Ross Rogers

masalah diselesaikan dalam 2 jawaban, masalahnya adalah pelingkupan atau pengangkatan variabel, apa pun Anda ingin menyebutnya.
— Zerkotin
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.