Bagaimana cara menunggu hingga respons berasal dari permintaan $ http, di angularjs?

93

Saya menggunakan beberapa data yang berasal dari layanan RESTful di beberapa halaman. Jadi saya menggunakan pabrik sudut untuk itu. Jadi, saya harus mendapatkan data sekali dari server, dan setiap kali saya mendapatkan data dengan layanan yang ditentukan itu. Sama seperti variabel global. Berikut contohnya:

var myApp =  angular.module('myservices', []);

myApp.factory('myService', function($http) {
    $http({method:"GET", url:"/my/url"}).success(function(result){
        return result;
    });
});

Di pengontrol saya, saya menggunakan layanan ini sebagai:

function myFunction($scope, myService) {
    $scope.data = myService;
    console.log("data.name"+$scope.data.name);
}

Ini bekerja dengan baik untuk saya sesuai kebutuhan saya. Tetapi masalahnya di sini adalah, ketika saya memuat ulang di halaman web saya, layanan akan dipanggil lagi dan meminta server. Jika di antara beberapa fungsi lain dijalankan yang bergantung pada "layanan yang ditentukan", Ini memberikan kesalahan seperti "sesuatu" yang tidak ditentukan. Jadi saya ingin menunggu di skrip saya sampai layanan dimuat. Bagaimana saya bisa melakukan itu? Apakah ada cara melakukannya di angularjs?

anilCSE
sumber

Jawaban:

150

Anda harus menggunakan promise untuk operasi asinkron yang tidak Anda ketahui kapan akan diselesaikan. Janji "mewakili operasi yang belum selesai, tetapi diharapkan di masa mendatang." ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise )

Contoh implementasi akan seperti:

myApp.factory('myService', function($http) {

    var getData = function() {

        // Angular $http() and then() both return promises themselves 
        return $http({method:"GET", url:"/my/url"}).then(function(result){

            // What we return here is the data that will be accessible 
            // to us after the promise resolves
            return result.data;
        });
    };


    return { getData: getData };
});


function myFunction($scope, myService) {
    var myDataPromise = myService.getData();
    myDataPromise.then(function(result) {  

       // this is only run after getData() resolves
       $scope.data = result;
       console.log("data.name"+$scope.data.name);
    });
}

Sunting: Mengenai komentar Sujoys bahwa Apa yang harus saya lakukan sehingga panggilan myFuction () tidak akan kembali sampai fungsi .then () menyelesaikan eksekusi.

function myFunction($scope, myService) { 
    var myDataPromise = myService.getData(); 
    myDataPromise.then(function(result) { 
         $scope.data = result; 
         console.log("data.name"+$scope.data.name); 
    }); 
    console.log("This will get printed before data.name inside then. And I don't want that."); 
 }

Anggap saja panggilan ke getData () membutuhkan waktu 10 detik untuk selesai. Jika fungsi tidak mengembalikan apa pun dalam waktu itu, itu akan secara efektif menjadi kode sinkron normal dan akan menggantung browser sampai selesai.

Dengan janji yang kembali seketika, sementara itu browser bebas untuk melanjutkan dengan kode lain. Setelah janji diselesaikan / gagal, panggilan then () dipicu. Jadi jauh lebih masuk akal dengan cara ini, bahkan jika itu mungkin membuat aliran kode Anda sedikit lebih kompleks (kompleksitas adalah masalah umum pemrograman asinkron / paralel secara umum!)

mikel
sumber
2
Ini memecahkan masalah saya !!! Untuk orang lain, saya memiliki dropdown yang membutuhkan data dari panggilan ajax, jadi ketika cakupan dibuat, data tidak tersedia. Dengan penangguhan ini, cakupan dapat ditetapkan untuk memiliki data yang berasal dari panggilan ajax.
Kat Lim Ruiz
8
@ mikel: Saya punya pertanyaan lain di sini. Panggilan myFuction () Anda akan segera kembali tetapi janji itu .then () akan dipanggil nanti. Apa yang harus saya lakukan agar panggilan myFuction () tidak akan kembali sampai fungsi .then () menyelesaikan eksekusi. function myFunction($scope, myService) { var myDataPromise = myService.getData(); myDataPromise.then(function(result) { $scope.data = result; console.log("data.name"+$scope.data.name); }); console.log("This will get printed before data.name inside then. And I don't want that."); }
Sujoy
13

bagi orang yang baru mengenal ini, Anda juga dapat menggunakan panggilan balik misalnya:

Dalam layanan Anda:

.factory('DataHandler',function ($http){

   var GetRandomArtists = function(data, callback){
     $http.post(URL, data).success(function (response) {
         callback(response);
      });
   } 
})

Di pengontrol Anda:

    DataHandler.GetRandomArtists(3, function(response){
      $scope.data.random_artists = response;
   });
Raul Gomez
sumber
Solusi bagus. Sedang memikirkan hal yang sama ini ketika saya melihatnya. Senang seseorang meletakkan ini di luar sana.
Nate
0

FYI, ini menggunakan Angularfire sehingga mungkin sedikit berbeda untuk layanan yang berbeda atau penggunaan lain tetapi harus menyelesaikan isse yang sama dengan $ http. Saya memiliki masalah yang sama, satu-satunya solusi yang paling cocok untuk saya adalah menggabungkan semua layanan / pabrik menjadi satu janji pada ruang lingkup. Pada setiap rute / tampilan yang membutuhkan layanan ini / dll untuk dimuat, saya meletakkan fungsi apa pun yang memerlukan data yang dimuat di dalam fungsi pengontrol yaitu myfunct () dan app.js utama dijalankan setelah auth saya letakkan

myservice.$loaded().then(function() {$rootScope.myservice = myservice;});

dan dalam tampilan yang baru saja saya lakukan

ng-if="myservice" ng-init="somevar=myfunct()"

di first / parent view element / wrapper sehingga pengontrol dapat menjalankan semua yang ada di dalamnya

myfunct()

tanpa mengkhawatirkan masalah janji / pesanan / antrian async. Saya harap itu membantu seseorang dengan masalah yang sama dengan saya.

Samuel Barney
sumber
0

Saya mengalami masalah yang sama dan tidak ada jika ini berhasil untuk saya. Inilah yang berhasil ...

app.factory('myService', function($http) {
    var data = function (value) {
            return $http.get(value);
    }

    return { data: data }
});

dan kemudian fungsi yang menggunakannya adalah ...

vm.search = function(value) {

        var recieved_data = myService.data(value);

        recieved_data.then(
            function(fulfillment){
                vm.tags = fulfillment.data;
            }, function(){
                console.log("Server did not send tag data.");
        });
    };

Layanan ini tidak terlalu penting tetapi saya pikir ini adalah praktik yang baik untuk ekstensibilitas. Sebagian besar dari apa yang Anda perlukan untuk satu keinginan untuk yang lain, terutama saat menggunakan API. Pokoknya saya harap ini membantu.

pengguna3127557
sumber