Angular $q возвращает обещание нескольких вызовов $http

Я работаю над вызовом $http, который перебирает каждый из нескольких API и возвращает все данные в одном объекте. Обычно у меня есть обещание, готовое к разрешению, когда был сделан вызов $http. Подобно этому:

function getAllData(api) {
    return $http({
        method: 'GET',
        url: '/api/' + api
    })
    .then(sendResponseData)
    .catch (sendGetVolunteerError);
}

Текущая функция у меня есть циклы по каждому API и помещает каждый объект в API в массив, а затем помещает его в общий массив. У меня это функционировало, возвращая многомерный массив, который нужно было сгладить.

Я хотел бы вернуть это обещанием, но возвращаю undefined. Вот что у меня пока? Есть ли лучший способ подойти к этому?

служба данных:

function getSearchData() {
    return {
        loadDataFromUrls: function () {
            var apiList = ["abo", "ser", "vol", "con", "giv", "blo", "par"];
            var deferred = $q.defer();
            var log = [];
            angular.forEach(apiList, function (item, key) {
                var logNew = [];
                $http({
                    method: 'GET',
                    url: '/api/' + item
                }).then(function (response) {
                    angular.forEach(response.data, function (item, key) {
                        this.push(item);
                    }, logNew);
                    return logNew;
                });
                this.push(logNew);
            }, log);
            $q.all(log).then(

            function (results) {
                deferred.resolve(
                JSON.stringify(results))
            },

            function (errors) {
                deferred.reject(errors);
            },

            function (updates) {
                deferred.update(updates);
            });
            return deferred.promise;
        }
    };
};

Контроллер:

function getSearchData(){
  return dataService.getSearchData.loadDataFromUrls;
}  

$scope.searchData = getSearchData();

person byrdr    schedule 20.03.2015    source источник
comment
Я не уверен на 100%, что здесь происходит - вы не подталкиваете пустые массивы (logNew) к массиву log, которого хотите дождаться? Разве вы не должны вместо этого нажимать promise, возвращаемый $http?   -  person Thor Jacobsen    schedule 20.03.2015


Ответы (3)


Здесь вам нужны функции $q.all и map:

function getSearchData() {
    return {
        // returns a promise for an object like:
        // { abo: resultFromAbo, ser: resultFromSer, ... }
        loadDataFromUrls: function () {
            var apiList = ["abo", "ser", "vol", "con", "giv", "blo", "par"];

            return $q.all(apiList.map(function (item) {
                return $http({
                    method: 'GET',
                    url: '/api/' + item
                });
            }))
            .then(function (results) {
                var resultObj = {};
                results.forEach(function (val, i) {
                    resultObj[apiList[i]] = val.data;
                });
                return resultObj;        
            });
        }
    };
}
person JLRishe    schedule 20.03.2015
comment
По какой-то причине я возвращаю значение undefined в контроллере: function getSearchData(){ return dataService.getSearchData.loadDataFromUrls; } console.log(getSearchData()); - person byrdr; 20.03.2015
comment
@byrdr Это потому, что getSearchData возвращает объект с loadDataFromUrls в качестве одного из его свойств, но вы получаете доступ к loadDataFromUrls как к свойству getSearchData. Попробуйте: function getSearchData(){ return dataService.getSearchData().loadDataFromUrls; } console.log(getSearchData()); Это оставляет вопрос о том, почему у вас есть дополнительный слой между вашей службой данных и вашей getSearchData функцией. - person JLRishe; 20.03.2015
comment
Это имеет смысл, спасибо. Приведенный выше код в настоящее время регистрирует саму функцию. - person byrdr; 20.03.2015
comment
обертывание его в iffe возвращает объект обещания {затем: функция, поймать: функция, наконец: функция} - person byrdr; 20.03.2015
comment
@byrdr Да, вам понадобится еще одна пара скобок, чтобы назвать это: console.log(getSearchData()());. Но я думаю, что вы задействуете слишком много слоев функций. Вы можете просто сделать: var getSearchData = dataService.getSearchData().loadDataFromUrls; console.log(getSerachData());. Это должно зарегистрировать обещание. - person JLRishe; 20.03.2015
comment
@byrdr Да, когда у вас есть объект обещания, вы можете вызвать для него .then(), чтобы получить доступ к результату. И здесь не нужно привлекать ИИФЭ. У вас уже слишком много функциональных слоев, и добавление дополнительных — не выход. :) - person JLRishe; 20.03.2015
comment
Все работает правильно. Спасибо за вашу помощь. - person byrdr; 20.03.2015
comment
Я уже какое-то время ищу что-то подобное. Знал о $q, но не о карте. Отлично работает на моих фабриках. Спасибо! - person Motoman; 05.08.2016

Если у вас есть произвольный набор вызовов API, я бы сделал что-то вроде этого:

function getSearchData(){
    var deferred = $q.defer();
    var noOfCalls = apiList.length;
    var results = [];
    var called = 0;

    angular.forEach(apiList, function(item, key) {
        $http.get(url).then(function(result){
           results.push(result);
           called++;
           if(called == noOfCalls){
              deferred.resolve(results);
           }     
        })
   });

    return deferred.promise;
}

Однако, если вы знаете, что представляет собой каждый вызов API, лучше использовать $.all таким образом.

function search1(){
      return $http.get(search1Url).then(function(result){
          // do something to it
          return result; 
      });
}

function search2(){
      return $http.get(search2Url).then(function(result){
          // do something to it
          return result; 
      });
}

function search3(){
      return $http.get(search3Url).then(function(result){
          // do something to it
          return result; 
      });
}

function search4(){
      return $http.get(search4Url).then(function(result){
          // do something to it
          return result; 
      });
}

function getSearchResult(){

    return $q.all([search1(), search2(), search3(), search4()]).then(function(results){
       // OPTIONAL  aggregate results before resolving
       return results;
    });
}
person Chris Noring    schedule 20.03.2015
comment
Вы имели в виду $q.defer()? - person GregL; 20.03.2015
comment
@JLRishe, спасибо, что указали на анти-шаблон. Сегодня я кое-что узнал.. ура, Крис - person Chris Noring; 20.03.2015
comment
@Chris Рад протянуть руку помощи. Вы все еще используете его в getSearchResult, и вам нужно передать в $q.all здесь search1(), search2() и т. д., а не только search1, search2. А другим вашим функциям нужны операторы return, потому что они ничего не возвращают. - person JLRishe; 20.03.2015
comment
У вас все еще есть дополнительный оператор возврата в getSearchResult, но вот голос. :) - person JLRishe; 20.03.2015

Вы должны добавить список промисов в $q (неразрешенные промисы, как в вашем коде), который является $promise service

Пример:

var firstPromise = service1.getMethod1().$promise;
var secondPromise = service2.getMethod2().$promise;
$q.all([firstPromise, secondPromise]).then(function(dataList){
     // dataList[0] will be result of `firstPromise`
     // dataList[1] will be result of `secondPromise`
});
person Cosmin    schedule 20.03.2015