$ q.all с рекурсией?

У меня есть функция, выполняющая рекурсивные вызовы. Структура выглядит примерно так:

var getData = function(data){
  makeDbCall().then(function(response){
    if(response.something === someCondition){
      foreach(object in response)
        getData(object ); //calls itself over and over 
      }
    }
  )
}; 

Общая идея (чтобы не быть не по теме) состоит в том, чтобы, скажем, получить все отели в городе, все номера в этих отелях, всю мебель в этих номерах и т. Д. Обычный вызов $ q.all foreach похож на this - функция определяет:

var promises = [];

а затем возвращает $ q.all:

return $q.all(promises);

Моя проблема в том, что я не знаю, откуда вернуть $ q.all - если я верну его из "getData", он вернется на первой итерации рекурсии и (как я понимаю) может не включать последующие итерации, если первоначальные вызовы разрешаются до того, как будут отправлены новые.

Если я верну его ПОСЛЕ вызова getData(), я получу ту же проблему, потому что, как мы все знаем, javascript продолжает работу, не дожидаясь вызовов, если мы не отложим.

Я понимаю, что это расплывчатый вопрос, и меня вполне устраивает расплывчатый ответ. Я просто пытаюсь понять, не упускаю ли я чего-то абсолютно простого или это невозможно. (Я знаю, как это сделать очень грязно, но мне бы очень хотелось как-нибудь использовать $ q.all).

Вот фактическая функция, которую я пытаюсь дождаться с измененными значениями вызовов для примера.


person VSO    schedule 17.12.2015    source источник
comment
Вы хотите, чтобы функции getData выполнялись последовательно или параллельно? Возвращают ли getData функции обещания?   -  person georgeawg    schedule 17.12.2015
comment
Параллельно. В противном случае я бы просто связал их, как предложил Витторе. makeDbCall () автоматически является обещанием, независимо от того, может ли его http или Restangular, getData вернуть $ q.all (promises), вопрос в том, должен ли он. Думаю, я просто собираюсь сделать это по-гетто и сравнить количество с длиной возвращаемого списка объектов.   -  person VSO    schedule 17.12.2015


Ответы (3)


Я попробовал кое-что, вложив разрешение обещания, например следующее:

function getData(){
  return new Promise((resolve, reject)){
    for(let item of response){
      getData(item).then((res)=>{
        resolve(res);
      },(err)=>{
        reject(err);
      });
    }
  }
}

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

person mfrachet    schedule 17.12.2015
comment
Спасибо за ответ. Я не уверен, чем это отличается от присвоения вызова переменной и помещения его в массив обещаний. Может быть, я слишком много думаю, и все же все будет в цепочке. Тестирование. - person VSO; 17.12.2015

Одна из вещей, которые вы можете сделать, - это связать обещания в исходном массиве обещаний и использовать карту для получения обещаний для каждого элемента в массиве, т.е. рассмотреть этот фрагмент

function getHotels(city) { return ajaxPromise ...  }
function getRooms(hotel) { ... } 
function getFurniture(room) { ... } 
function showUI(furniture) { ... }

getHotels(city).then(function(hotels) {
  return $q.all(hotels.map( h => getRooms(h)))
}).then(function(hotelRooms) {
  return $q.all(hotelRooms.map (hr => getFurniture(hr)))
}).then(showUI)
person vittore    schedule 17.12.2015
comment
Я мог бы в конечном итоге сделать это, но, насколько я понимаю, это будет намного медленнее, поскольку в этом случае не так быстро рассылаются спам-звонки. Ценю ответ! - person VSO; 17.12.2015

Для начинающих ---

Эта методология устарела

app.factory('json',function($q,$http){
    return function(files){
        var promises = [];
        angular.forEach(files, function(file){
            var deffered  = $q.defer();
            $http({
                   url : file,
                   method: 'GET'
            }).success(function(data){
                   deffered.resolve(data);
            }).error(function(error){
                   deffered.reject();
            });
            promises.push(deffered.promise);
        }) 
        return $q.all(promises);
    }
});

.success и .error методы $http устарели. Для получения дополнительной информации см. https://docs.angularjs.org/api/ng/service/$http.

Предпочтительный способ:

app.factory('json',function($q,$http){
    return function(files){
        var promises = [];
        angular.forEach(files, function(file){
            var promise =
                $http({
                       url : file,
                       method: 'GET'
                      })
            promises.push(promise);
        }) 
        return promises;
    }
});

Затем вы можете обработать каждое из возвращенных обещаний по отдельности или использовать $q.all. Имейте в виду, что $q.all не является устойчивым. Первое отклоненное обещание пропустит метод .then, и только первое отклонение будет обнаружено методом .catch.

var promiseList = json(files);

$q.all(promiseList).then (function (responseList) {
     //executes only if all promises ok
     $scope.dataList = doSomethingWith(responseList);
}). catch (error) { function (error)
     //executes with first error
     //log error
});

Чтобы обработать обещание индивидуально:

promiseList[0].then (function (result) {
                   //save data
                   $scope.dataList[0] = result.data;
            }).catch (function (error) {
                   //log error
            });

Обратите внимание, что методы .then и .catch возвращают данные иначе, чем методы .success и .error.

person georgeawg    schedule 17.12.2015