AngularJS: Ожидание асинхронного вызова

Я не могу склонить голову вокруг концепции AngularJS promises.

У меня есть провайдер:

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

packingProvider.provider('packingProvider',function(){
    return{
       $get: function($http){
           return{
              getPackings: function(){
                  $http.post('../sys/core/fetchPacking.php').then(function(promise){
                      var packings = promise.data;
                      return packings;
                  });
              }
           }
       }
   }
});

Как вы можете видеть, это обеспечивает метод getPackings(), который вернет объект

Теперь, если я использую это в своем основном приложении для получения данных, вызов будет асинхронным, что приведет к ошибке, когда мне придется "ждать" данных:

var packings = packingProvider.getPackings();

console.log(packings); // undefined

Как мне сделать это без рефакторинга процесса в моем основном контроллере?

Ответ 1

Возвращаемое значение из вашего метода then не возвращается, если вы считаете это; он возвращается позже.

В заявлении var packings = packingProvider.getPackings(); возвращаемое значение undefined, потому что обещание, возвращенное из $http, является асинхронным. Это означает, что вызов $http.post происходит, не завершается, тогда ваша функция возвращается. В функциях JS, которые ничего не возвращают, возвращаются undefined. Вызов post завершается позже и выполняет return packings;, который возвращается в никуда.

Метод getPackings должен, вероятно, возвращать обещание от $http.post напрямую. Таким образом, любой код, который хочет использовать этот метод, может называть then по обещанию напрямую и устанавливать значение так, как ему нужно. В контроллере вы можете назначить это обещание непосредственно $scope и использовать его в своем представлении. Вот хорошая статья, которая объясняет эту особенность: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/

Кстати, похоже, у вас там длинная декларация сервиса; любая причина не сокращать его до чего-то подобного?

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

module.service('packing', function($http) {
  return {
    getPackings: function() {
      return $http.post('../sys/core/fetchPacking.php');
    }
  };
});

Я относительно новичок в AngularJS, но я не вижу никакой выгоды во всем, что печатает. (=

Ответ 2

Try

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

packingProvider.provider('packingProvider',function(){
    return{
       $get: function($http){
           return{
              getPackings: function(){
                  return $http.post('../sys/core/fetchPacking.php').then(function(response){
                      return response.data; // packings
                  });
              }
           }
       }
   }
});

Тогда

packingProvider.getPackings().then(function(packings){
    console.log(packings);
});

Основная идея: вам нужно вернуть объект обещания из функции getPackings. Не уверен, что вы можете сделать вызов синхронным, но это почти не очень хорошая идея. Кроме того, если вы хотите сделать "упаковку" модельным объектом на вашем контроллере и использовать для двунаправленного привязки, вы можете безопасно назначить объект обещания, который Angular будет разрешен, как только данные пройдут:

$scope.packings = packingProvider.getPackings();

UPDATE

Начиная с версии 1.2.0-rc.3, обезвреживание обещаний устарело (https://github.com/angular/angular.js/blob/master/CHANGELOG.md#120-rc3-ferocious-twitch-2013-10-14), поэтому строка кода выше не будет в результате получается двунаправленное связывание.

Ответ 3

Существует масса доступных ресурсов, которые показывают, как использовать Deferreds/ Promises с jQuery. Попробуйте найти те, которые могут помочь вам преодолеть это препятствие. Afaik, AngularJS promises являются такими же, как jQuery promises.

В jQuery обещание используется примерно так:

var getPackings = function() { return $.get('../sys/core/fetchPacking.php'); };
var packings;
$.when(getPackings()).then(function(data){ packings = data; console.log(packings) });

Имейте в виду, что jQuery-вызовы ajax имеют такие функции, как .done,.success и т.д., которые заменяют общие функции Deferreds.when(). then().

В приведенном выше методе jQuery вы можете видеть, что мы должны установить данные и вывести их в функцию .then(), потому что вы не можете гарантировать, что асинхронный процесс выполняется в другом месте. Поэтому в идеале вы должны вызвать любую функцию, которая продолжит вашу обработку в функции .then(), например:

$.when(myAsyncFunc()).then(myAppCanContinue(dataReturnedByAsyncFunc));

Angular будет следовать той же логике. Если вы понимаете вышеизложенное, должно быть проще понять, как это сделать в Angular. Кроме того, ознакомьтесь с этой статьей, в которой приведены простые примеры: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/

Чтобы заставить ваш код работать, вам нужно будет сделать что-то вроде этого:

packingProvider.provider('packingProvider',function(){
    return{
       $get: function($http){
           return{
              getPackings: function(){
                  return $http.post('../sys/core/fetchPacking.php');
              }
           }
       }
   }
});
var packings = packingProvider.getPackings();

packings.then(function(data) { console.log(data)});