Angular обещание для нескольких $http

Я пытаюсь выполнить несколько вызовов $http, и мой код выглядит примерно так:

var data = ["data1","data2","data3"..."data10"];

for(var i=0;i<data.length;i++){
    $http.get("http://example.com/"+data[i]).success(function(data){
        console.log("success");
    }).error(function(){
        console.log("error");
    });
}

Как я могу получить обещание узнать, что вызов $http успешный? Если кто-то из этого не сработает, выполните некоторые действия.

Ответ 1

Вы также можете использовать метод $q.all().

Итак, из вашего кода:

var data = ["data1","data2","data3"..."data10"];

for(var i=0;i<data.length;i++){
    $http.get("http://example.com/"+data[i]).success(function(data){
        console.log("success");
    }).error(function(){
        console.log("error");
    });
}

Вы можете сделать:

var promises = [];
data.forEach(function(d) {
  promises.push($http.get('/example.com/' + d))
});
$q.all(promises).then(function(results){
  results.forEach(function(data,status,headers,config){
    console.log(data,status,headers,config);
  })
}),

Это в основном означает выполнение целых запросов и определение поведения, когда все они были завершены.

В предыдущем комментарии:

Используя статус, вы можете узнать, поступили ли какие-либо ошибки. Также вы можете настроить другую конфигурацию для каждого запроса, если это необходимо (например, тайм-ауты, например).

Если кто-то из этого отказался, выполнит какое-то действие.

Из документов, которые также на основе спецификаций A +:

$q.all(successCallback, errorCallback, notifyCallback);

Ответ 2

Если вы хотите вырваться на первую ошибку, вам необходимо сделать синхронный цикл для вашего цикла: Angular синхронный цикл http для обновления индикатора выполнения

var data = ["data1", "data2", "data3", "data10"];
$scope.doneLoading = false;
var promise = $q.all(null);

angular.forEach(data, function(url){
  promise = promise.then(function(){
    return $http.get("http://example.com/" + data[i])
      .then(function (response) {
        $scope.data = response.data;
      })
      .catch(function (response) {
        $scope.error = response.status;
      });
  });
});

promise.then(function(){
  //This is run after all of your HTTP requests are done
  $scope.doneLoading = true;
});

Если вы хотите, чтобы он был асинхронным, тогда: Как связать вызовы Angular $http.get()?

app.controller("AppCtrl", function ($scope, $http, $q) {
  var data = ["data1", "data2", "data3", "data10"];
  $q.all([
    for(var i = 0;i < data.length;i++) {
      $http.get("http://example.com/" + data[i])
        .then(function (response) {
          $scope.data= response.data;
        })
        .catch(function (response) {
          console.error('dataerror', response.status, response.data);
          break;
        })
        .finally(function () {
          console.log("finally finished data");
        });
    }
  ]).
  then(function (results) {
    /* your logic here */
  });
};

Эта статья очень хороша: http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/

Ответ 3

Принятый ответ в порядке, но он все еще немного уродлив. У вас есть массив вещей, которые вы хотите отправить.. вместо использования цикла for, почему бы не использовать Array.prototype.map?

var data = ["data1","data2","data3"..."data10"];

for(var i=0;i<data.length;i++){
    $http.get("http://example.com/"+data[i]).success(function(data){
        console.log("success");
    }).error(function(){
        console.log("error");
    });
}

Это становится

var data = ['data1', 'data2', 'data3', ...., 'data10']
var promises = data.map(function(datum) {
  return $http.get('http://example.com/' + datum)
})
var taskCompletion = $q.all(promises)
// Usually, you would want to return taskCompletion at this point,
// but for sake of example

taskCompletion.then(function(responses) {
  responses.forEach(function(response) {
    console.log(response)
  })
})

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

Одно слово предупреждения на success vs error - success и error больше похоже на обратные вызовы и являются предупреждениями, которые вы не знаете, как обетование работает/не использует его правильно. Promises then и catch свяжет и вернет новое обещание, заключающее в себе цепочку до сих пор, что очень полезно. Кроме того, использование success и error (где-либо еще, кроме сайта вызова $http), является запахом, потому что это означает, что вы явно полагаетесь на обещание HTTP Angular, а не на обещание, совместимое с A +.

Другими словами, постарайтесь не использовать success/error - для них редко возникает причина, и они почти всегда указывают на запах кода, потому что они вводят побочные эффекты.


Что касается вашего комментария:

Я сделал свой собственный очень простой эксперимент на $q.all. Но это только срабатывает, когда весь запрос является успешным. Если это произойдет, ничего не произойдет.

Это потому, что контракт all заключается в том, что он либо решает, если каждое обещание было успешным, либо отклонило, если хотя бы один был неудачным.

К сожалению, Angular встроенный $q сервис имеет только all; если вы хотите отклонить Promises, не вызывать результирующее обещание отклонить, тогда вам нужно будет использовать allSettled, который присутствует в большинстве основных библиотек обещаний (таких как Bluebird и исходный Q by kriskowal). Другая альтернатива - сворачивать свои собственные (но я бы предложил Bluebird).