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?


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.


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

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


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

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


Twoja getStuffmetoda polega na użyciu odroczonego antywzoru
Bergi,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.