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.