Bisakah Anda menyelesaikan janji angularjs sebelum mengembalikannya?

125

Saya mencoba untuk menulis fungsi yang mengembalikan sebuah janji. Tetapi ada kalanya informasi yang diminta segera tersedia. Saya ingin membungkusnya dengan janji agar konsumen tidak perlu mengambil keputusan.

function getSomething(id) {
    if (Cache[id]) {
        var deferred = $q.defer();
        deferred.resolve(Cache[id]); // <-- Can I do this?
        return deferred.promise;
    } else {
        return $http.get('/someUrl', {id:id});
    }
}

Dan gunakan seperti ini:

somethingService.getSomething(5).then(function(thing) {
    alert(thing);
});

Masalahnya adalah callback tidak dijalankan untuk janji yang diselesaikan sebelumnya. Apakah ini hal yang sah untuk dilakukan? Adakah cara yang lebih baik untuk menangani situasi ini?

Craig Celeste
sumber
10
Cara yang lebih sederhana untuk menulis pengembalian dalam kasus pertama adalah return $q.when(Cache[id]). Bagaimanapun, ini harus berfungsi dan memanggil callback setiap kali karena Anda membuat janji baru setiap kali.
musically_ut
1
Crud. Satu jam hidup saya hilang. Saya mencoba ini dalam tes unit dan janji terpenuhi setelah tes selesai, dan saya tidak melihatnya. Masalah dengan pengujian saya dan bukan kodenya.
Craig Celeste
Pastikan Anda memanggil $ scope. $ Apply () untuk memastikan semuanya langsung teratasi selama pengujian.
dtabuenc
Saya pikir akun httpbackend.flush untuk ini tetapi $ q mungkin tidak. Saya tidak menggunakan ruang lingkup dalam tes ini. Saya sedang menguji layanan secara langsung, tetapi saya tetap berhasil, terima kasih.
Craig Celeste

Jawaban:

174

Jawaban singkat: Ya, Anda dapat menyelesaikan janji AngularJS sebelum mengembalikannya, dan itu akan berperilaku seperti yang Anda harapkan.

Dari JB Nizet's Plunkr tetapi refactored untuk bekerja dalam konteks apa yang awalnya diminta (yaitu panggilan fungsi ke layanan) dan sebenarnya di situs.

Di dalam layanan ...

function getSomething(id) {
    // There will always be a promise so always declare it.
    var deferred = $q.defer();
    if (Cache[id]) {
        // Resolve the deferred $q object before returning the promise
        deferred.resolve(Cache[id]); 
        return deferred.promise;
    } 
    // else- not in cache 
    $http.get('/someUrl', {id:id}).success(function(data){
        // Store your data or what ever.... 
        // Then resolve
        deferred.resolve(data);               
    }).error(function(data, status, headers, config) {
        deferred.reject("Error: request returned status " + status); 
    });
    return deferred.promise;

}

Di dalam pengontrol ....

somethingService.getSomething(5).then(    
    function(thing) {     // On success
        alert(thing);
    },
    function(message) {   // On failure
        alert(message);
    }
);

Saya harap ini membantu seseorang. Saya tidak menemukan jawaban lain dengan sangat jelas.

h. mantel
sumber
2
Saya tidak bisa menjelaskan dengan kata-kata betapa bahagianya saya, Anda menghemat banyak waktu saya h. Mantel!
rilar
Jika http GET gagal, janji yang dikembalikan tidak ditolak dengan cara ini.
lex82
5
Jadi tl; dr untuk posting ini adalah: Ya, Anda dapat menyelesaikan janji sebelum mengembalikannya, dan itu akan korsleting sebagaimana dimaksud.
ray
1
Jawaban ini juga berlaku untuk Kris Kowal Q yang menjadi dasar janji Angular.
Keith
Saya menambahkan contoh penanganan kesalahan ke jawaban Anda, saya harap tidak apa-apa.
Simon East
98

Cara mengembalikan janji yang telah diselesaikan sebelumnya di Angular 1.x

Janji terselesaikan:

return $q.when( someValue );    // angular 1.2+
return $q.resolve( someValue ); // angular 1.4+, alias to `when` to match ES6

Janji yang ditolak:

return $q.reject( someValue );
Andrey Mikhaylov - lolmaus
sumber
1
Tidak perlu pabrik itu, fungsi helper tersebut sudah tersedia:{resolved: $q.when, rejected: $q.reject}
Bergi
Hai Bergi, terima kasih atas kontribusi Anda yang berharga. Saya telah mengedit jawaban saya.
Andrey Mikhaylov - lolmaus
2
Saya pikir jawaban ini harus dipilih.
Morteza Tourani
@mortezaT Jika dipilih, saya tidak akan mendapatkan lencana emas. ;)
Andrey Mikhaylov - lolmaus
6

Inilah cara saya biasanya melakukannya jika saya ingin benar-benar menyimpan data dalam cache atau objek

app.factory('DataService', function($q, $http) {
  var cache = {};
  var service= {       
    getData: function(id, callback) {
      var deffered = $q.defer();
      if (cache[id]) {         
        deffered.resolve(cache[id])
      } else {            
        $http.get('data.json').then(function(res) {
          cache[id] = res.data;              
          deffered.resolve(cache[id])
        })
      }
      return deffered.promise.then(callback)
    }
  }

  return service

})

DEMO

charlietfl.dll
sumber
0

Anda lupa menginisialisasi elemen Cache

function getSomething(id) {
    if (Cache[id]) {
        var deferred = $q.defer();
        deferred.resolve(Cache[id]); // <-- Can I do this?
        return deferred.promise;
    } else {
        Cache[id] = $http.get('/someUrl', {id:id});
        return Cache[id];
    }
}
zs2020
sumber
Maaf. Itu benar. Saya mencoba menyederhanakan kode untuk kejelasan dalam pertanyaan. Meski begitu, jika itu menjadi janji yang telah diselesaikan sebelumnya, sepertinya itu tidak memanggil panggilan balik.
Craig Celeste
2
Saya tidak berpikir jika Anda menyelesaikan janji dengan janji, janji batiniah diratakan. Ini akan mengisi Cachedengan promise alih-alih objek yang dimaksudkan dan tipe kembalian untuk kasus-kasus ketika sebuah objek ada di Cache dan jika tidak, tidak akan sama. Ini lebih tepat, menurut saya:$http.get('/someUrl', {id: id}).then(function (response) { Cache[id] = response.data; return Cache[id]; });
musically_ut
0

Saya suka menggunakan pabrik untuk mendapatkan data dari sumber daya saya seperti.

.factory("SweetFactory", [ "$http", "$q", "$resource", function( $http, $q, $resource ) {
    return $resource("/sweet/app", {}, {
        "put": {
            method: "PUT",
            isArray: false
        },"get": {
            method: "GET",
            isArray: false
        }
    });
}]);

Kemudian paparkan model saya dalam layanan seperti ini di sini

 .service("SweetService",  [ "$q", "$filter",  "$log", "SweetFactory",
    function ($q, $filter, $log, SweetFactory) {

        var service = this;

        //Object that may be exposed by a controller if desired update using get and put methods provided
        service.stuff={
            //all kinds of stuff
        };

        service.listOfStuff = [
            {value:"", text:"Please Select"},
            {value:"stuff", text:"stuff"}];

        service.getStuff = function () {

            var deferred = $q.defer();

          var promise = SweetFactory.get().$promise.then(
                function (response) {
                    if (response.response.result.code !== "COOL_BABY") {
                        deferred.reject(response);
                    } else {
                        deferred.resolve(response);
                        console.log("stuff is got", service.alerts);
                        return deferred.promise;
                    }

                }
            ).catch(
                function (error) {
                    deferred.reject(error);
                    console.log("failed to get stuff");
                }
            );

            promise.then(function(response){
                //...do some stuff to sett your stuff maybe fancy it up
                service.stuff.formattedStuff = $filter('stuffFormatter')(service.stuff);

            });


            return service.stuff;
        };


        service.putStuff = function () {
            console.log("putting stuff eh", service.stuff);

            //maybe do stuff to your stuff

            AlertsFactory.put(service.stuff).$promise.then(function (response) {
                console.log("yep yep", response.response.code);
                service.getStuff();
            }).catch(function (errorData) {
                alert("Failed to update stuff" + errorData.response.code);
            });

        };

    }]);

Kemudian pengontrol saya dapat memasukkannya dan mengeksposnya atau melakukan apa yang menurutnya benar dalam konteksnya hanya dengan mereferensikan Layanan yang diinjeksi.

Sepertinya bekerja dengan baik. Tapi saya agak baru mengenal sudut. * Penanganan kesalahan sebagian besar ditinggalkan untuk kejelasan

Frank Swanson
sumber
getStuffMetode Anda menggunakan antipattern yang ditangguhkan
Bergi