Czy możesz spełnić obietnicę angularjs, zanim ją zwrócisz?

125

Próbuję napisać funkcję, która zwraca obietnicę. Ale są chwile, kiedy żądane informacje są dostępne natychmiast. Chcę zawrzeć to w obietnicy, aby konsument nie musiał podejmować decyzji.

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

I użyj tego w ten sposób:

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

Problem polega na tym, że wywołanie zwrotne nie jest wykonywane dla wstępnie rozwiązanej obietnicy. Czy jest to uzasadnione? Czy jest lepszy sposób na rozwiązanie tej sytuacji?

Craig Celeste
źródło
10
Prostszym sposobem zapisania zwrotu w pierwszym przypadku jest return $q.when(Cache[id]). W każdym razie powinno to działać i wywoływać oddzwonienie za każdym razem, ponieważ za każdym razem tworzysz nowe obietnice.
muzycznie_ut
1
Crud. Stracona godzina mojego życia. Próbowałem tego w teście jednostkowym i obietnica została spełniona po zakończeniu testu i nie widziałem tego. Problem z moim testem, a nie kodem.
Craig Celeste
Upewnij się, że wywołujesz $ scope. $ Apply (), aby upewnić się, że problem zostanie rozwiązany od razu podczas testowania.
dtabuenc
Myślę, że httpbackend.flush kont do tego, ale $ q może nie. W tym teście nie używam zakresu. Testuję usługę bezpośrednio, ale i tak działa, dzięki.
Craig Celeste

Odpowiedzi:

174

Krótka odpowiedź: tak, możesz spełnić obietnicę AngularJS, zanim ją zwrócisz, i będzie ona działać zgodnie z oczekiwaniami.

Z Plunkr JB Nizeta, ale refaktoryzowany do pracy w kontekście tego, o co pierwotnie pytano (tj. Wywołanie funkcji do usługi) i faktycznie na miejscu.

Wewnątrz usługi ...

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;

}

Wewnątrz kontrolera ...

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

Mam nadzieję, że to komuś pomoże. Pozostałe odpowiedzi nie były dla mnie zbyt jasne.

h.coates
źródło
2
Nie potrafię opisać słowami, jak szczęśliwy jestem, zaoszczędziłeś mi tyle czasu h. Coates!
rilar
W przypadku niepowodzenia http GET zwrócona obietnica nie jest w ten sposób odrzucana.
lex82
5
Tak więc tl; dr dla tego postu brzmi: Tak, możesz rozwiązać obietnicę przed jej zwrotem, a ona spowoduje zwarcie zgodnie z przeznaczeniem.
ray
1
Ta odpowiedź dotyczy również Q Krisa Kowala, na którym opierają się obietnice Angulara.
Keith
Dodałem przykład obsługi błędów do Twojej odpowiedzi, mam nadzieję, że wszystko w porządku.
Simon East
98

Jak po prostu zwrócić wstępnie rozwiązaną obietnicę w Angular 1.x

Rozwiązana obietnica:

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

Odrzucona obietnica:

return $q.reject( someValue );
Andrey Mikhaylov - lolmaus
źródło
1
Nie potrzeba tej fabryki, te funkcje pomocnicze są już dostępne:{resolved: $q.when, rejected: $q.reject}
Bergi
Hej Bergi, dziękuję za cenną darowiznę. Odpowiednio zredagowałem odpowiedź.
Andrey Mikhaylov - lolmaus
2
Myślę, że ta odpowiedź powinna zostać wybrana.
Morteza Tourani
@mortezaT Gdyby został wybrany, nie dałby mi złotej plakietki. ;)
Andrey Mikhaylov - lolmaus
6

Oto, jak zwykle to robię, jeśli chcę faktycznie buforować dane w tablicy lub obiekcie

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
źródło
0

Zapomniałeś zainicjować element 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
źródło
Przepraszam. To prawda. Starałem się uprościć kod, aby zwiększyć jasność pytania. Mimo to, jeśli wchodzi w wstępnie rozwiązaną obietnicę, nie wydaje się wywoływać funkcji zwrotnej.
Craig Celeste
2
Nie sądzę, że jeśli rozwiążesz obietnicę obietnicą, wewnętrzna obietnica zostanie spłaszczona. Spowoduje to wypełnienie Cacheobietnicami zamiast zamierzonych obiektów i typem zwracanym w przypadkach, gdy obiekt znajduje się w pamięci podręcznej, a gdy nie jest, nie będzie taki sam. To jest bardziej poprawne, myślę, że:$http.get('/someUrl', {id: id}).then(function (response) { Cache[id] = response.data; return Cache[id]; });
muzycznie_ut
0

Lubię korzystać z fabryki, aby uzyskać dane z mojego zasobu na przykład.

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

Następnie wystaw mój model w serwisie takim jak ten tutaj

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

        };

    }]);

Wtedy moi kontrolerzy mogą go dołączyć i ujawnić lub zrobić to, co mu się podoba, jest to właściwe w jego kontekście, po prostu odwołując się do wstrzykniętej usługi.

Wydaje się działać dobrze. Ale jestem trochę nowy w kanciastym. * obsługa błędów przeważnie pomijana dla przejrzystości

Frank Swanson
źródło
Twoja getStuffmetoda polega na użyciu odroczonego antywzoru
Bergi,