Чи можете ви вирішити обіцянку angularjs перед тим, як повернути його?


125

Я намагаюся написати функцію, яка повертає обіцянку. Але бувають випадки, коли запитувана інформація стає доступною негайно. Я хочу вкласти це в обіцянку, щоб споживачеві не потрібно було приймати рішення.

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

І використовуйте його так:

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

Проблема полягає в тому, що зворотний виклик не виконується за попередньо вирішеною обіцянкою. Це законна справа? Чи є кращий спосіб вирішити цю ситуацію?


10
Більш простий спосіб написати повернення в першому випадку return $q.when(Cache[id]). Так чи інакше, це має спрацювати і викликати зворотний виклик кожного разу, оскільки ви створюєте нові обіцянки щоразу.
musically_ut


1
Сирий. Затрачена година мого життя. Я намагався це зробити в одиничному тесті, і обіцянка виконується після завершення тесту, і я його не бачив. Проблема з моїм тестом, а не кодом.
Крейг Селесте

Переконайтеся, що ви зателефонували в $ range. $ Apply (), щоб переконатися, що все вирішиться під час тестування.
dtabuenc

Я думаю, що httpbackend.flush обліковує це, але $ q не може. Я не використовую сферу застосування в цьому тесті. Я тестую послугу безпосередньо, але я все-таки працював, спасибі.
Крейг Селест

Відповіді:


174

Коротка відповідь: Так, ви можете вирішити обіцянку AngularJS, перш ніж повернути його, і воно буде вести себе так, як ви очікували.

З Plunkr від JB Nizet, але він відновив свою роботу в контексті того, що було спочатку запитано (тобто функціональний виклик до служби) і фактично на місці.

Всередині сервісу ...

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;

}

Всередині контролера ....

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

Я сподіваюся, що це комусь допоможе. Я не знайшов інших відповідей дуже чіткими.


2
Я не можу словами описати, як щасливий Ім, ти врятував мені стільки часу h.coates!
rilar

У разі невдачі http GET, повернута обіцянка не буде відхилена таким чином.
lex82

5
Отже, tl; dr для цієї посади: Так, ви можете вирішити обіцянку перед поверненням, і воно замикається за призначенням.
промінь

1
Ця відповідь стосується також питання Кріса Коваль, на якому ґрунтуються обіцянки Angular.
Кіт

До вашої відповіді я додав приклад обробки помилок, сподіваюся, що це нормально.
Саймон Схід

98

Як просто повернути попередньо розв’язану обіцянку в Angular 1.x

Вирішено обіцянку:

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

Відхилена обіцянка:

return $q.reject( someValue );

1
Немає потреби в цій фабриці, ці допоміжні функції вже доступні:{resolved: $q.when, rejected: $q.reject}
Бергі

Ей, Бергі, дякую за цінний внесок. Я відповідно відредагував свою відповідь.
Андрій Михайлов - lolmaus

2
Я думаю, цю відповідь слід вибрати.
Morteza Tourani

@mortezaT Якби його було вибрано, він не дав би мені золотого значка. ;)
Андрій Михайлов - lolmaus

6

Ось як я зазвичай це роблю, якщо хочу фактично кешувати дані в масиві чи об'єкті

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

Ви забули ініціалізувати елемент кеша

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

Вибачте. Це правда. Я намагався спростити код для ясності у питанні. Незважаючи на це, якщо він переходить до заздалегідь вирішеної обіцянки, він, схоже, не викликає зворотного дзвінка.
Крейг Селесте

2
Я не думаю, що якщо ти обіцяєш обіцянку обіцяти, внутрішня обіцянка вирівнюється. Це заповнить Cacheобіцянки замість призначених об'єктів і тип повернення для випадків, коли об’єкт знаходиться в кеші, і коли він не буде, не буде однаковим. Це правильніше, я думаю:$http.get('/someUrl', {id: id}).then(function (response) { Cache[id] = response.data; return Cache[id]; });
musically_ut

0

Мені подобається використовувати фабрику, щоб отримати дані з мого ресурсу чогось подібного.

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

Потім виставіть мою модель в службі, як ця тут

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

        };

    }]);

Тоді мої контролери можуть включити його та викрити його або зробити те, що в ньому є, правильно в його контексті, просто посилаючись на ін'єкційну службу.

Здається, працює добре. Але я щось нове до кутового. * поводження з помилками в основному залишається для ясності


Ваш getStuffметод використовує відкладений антипатерн
Бергі,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.