Подтвердить что ты не робот

Использование success/error/finally/catch с помощью Promises в AngularJS

Я использую $http в AngularJs, и я не уверен, как использовать возвращенное обещание и обрабатывать ошибки.

У меня есть этот код:

$http
    .get(url)
    .success(function(data) {
        // Handle data
    })
    .error(function(data, status) {
        // Handle HTTP error
    })
    .finally(function() {
        // Execute logic independent of success/error
    })
    .catch(function(error) {
        // Catch and handle exceptions from success/error/finally functions
    });

Это хороший способ сделать это, или есть более простой способ?

4b9b3361

Ответ 1

Promises являются абстракцией над операторами, которые позволяют нам синхронно выражать себя с асинхронным кодом. Они представляют собой выполнение одноразовой задачи.

Они также предоставляют обработку исключений, как и обычный код, вы можете вернуться с обещания или бросить.

Что вы хотите в синхронном коде:

try{
  try{
      var res = $http.getSync("url");
      res = someProcessingOf(res);
  } catch (e) {
      console.log("Got an error!",e);
      throw e; // rethrow to not marked as handled
  }
  // do more stuff with res
} catch (e){
     // handle errors in processing or in error.
}

Обещанная версия очень похожа:

$http.get("url").
then(someProcessingOf).
catch(function(e){
   console.log("got an error in initial processing",e);
   throw e; // rethrow to not marked as handled, 
            // in $q it better to `return $q.reject(e)` here
}).then(function(res){
    // do more stuff
}).catch(function(e){
    // handle errors in processing or in error.
});

Ответ 2

Забудьте об использовании методов success и error.

Оба метода устарели в angular 1.4. По сути, причина обескураживания заключается в том, что они не являются дружественными к цепочке, если можно так выразиться.

В следующем примере я попытаюсь продемонстрировать, что я имею в виду success и error, которые не являются дружественными к цепочке. Предположим, мы вызываем API, который возвращает объект пользователя с адресом:

Пользовательский объект:

{name: 'Igor', address: 'San Francisco'}

Вызов API:

$http.get('/user')
    .success(function (user) {
        return user.address;   <---  
    })                            |  // you might expect that 'obj' is equal to the
    .then(function (obj) {   ------  // address of the user, but it is NOT

        console.log(obj); // -> {name: 'Igor', address: 'San Francisco'}
    });
};

Что произошло?

Так как success и error возвращают оригинальное обещание, то есть тот, который возвращается $http.get, объект, переданный обратному вызову then, представляет собой весь пользовательский объект, который означает тот же самый вход для предыдущего обратного вызова success.

Если бы мы связали два then, это было бы менее запутанным:

$http.get('/user')
    .then(function (user) {
        return user.address;  
    })
    .then(function (obj) {  
        console.log(obj); // -> 'San Francisco'
    });
};

Ответ 3

Я думаю, что предыдущие ответы верны, но вот еще один пример (только fyi, success() и error() устарели в соответствии с AngularJS Главная страница:

$http
    .get('http://someendpoint/maybe/returns/JSON')
    .then(function(response) {
        return response.data;
    }).catch(function(e) {
        console.log('Error: ', e);
        throw e;
    }).finally(function() {
        console.log('This finally block');
    });

Ответ 4

Какой тип детализации вы ищете? Обычно вы можете:

$http.get(url).then(
  //success function
  function(results) {
    //do something w/results.data
  },
  //error function
  function(err) {
    //handle error
  }
);

Я обнаружил, что "finally" и "catch" лучше при соединении нескольких promises.

Ответ 5

В случае Angular $http функция success() и error() будет отключена отклика, поэтому обратная связь будет похожа на $http (...). success (функция (данные, статус, заголовки, config))

для then(), вы, вероятно, будете иметь дело с необработанным объектом ответа. например, опубликованные в документе API-интерфейса AngularJS $

$http({
        url: $scope.url,
        method: $scope.method,
        cache: $templateCache
    })
    .success(function(data, status) {
        $scope.status = status;
        $scope.data = data;
    })
    .error(function(data, status) {
        $scope.data = data || 'Request failed';
        $scope.status = status;
    });

Последний .catch(...) не понадобится, если в предыдущей цепочке обещаний не возникнет ошибка.

Ответ 6

Я делаю это, как Брэдли Брейтуэйт предлагает в своем blog:

app
    .factory('searchService', ['$q', '$http', function($q, $http) {
        var service = {};

        service.search = function search(query) {
            // We make use of Angular $q library to create the deferred instance
            var deferred = $q.defer();

            $http
                .get('http://localhost/v1?=q' + query)
                .success(function(data) {
                    // The promise is resolved once the HTTP call is successful.
                    deferred.resolve(data);
                })
                .error(function(reason) {
                    // The promise is rejected if there is an error with the HTTP call.
                    deferred.reject(reason);
                });

            // The promise is returned to the caller
            return deferred.promise;
        };

        return service;
    }])
    .controller('SearchController', ['$scope', 'searchService', function($scope, searchService) {
        // The search service returns a promise API
        searchService
            .search($scope.query)
            .then(function(data) {
                // This is set when the promise is resolved.
                $scope.results = data;
            })
            .catch(function(reason) {
                // This is set in the event of an error.
                $scope.error = 'There has been an error: ' + reason;
            });
    }])

Ключевые моменты:

  • Функция разрешения связывается с функцией .then в нашем контроллере, то есть все хорошо, поэтому мы можем сдержать свое обещание и разрешить его.

  • Функция отклонения ссылается на функцию .catch в нашем контроллере, то есть что-то пошло не так, поэтому мы не можем сдержать наше обещание и отвергайте его.

Это довольно стабильно и безопасно, и если у вас есть другие условия, чтобы отклонить обещание, вы всегда можете отфильтровать свои данные в функции успеха и вызвать deferred.reject(anotherReason) с целью отклонения.

Как предложил Райан Вирд в комментариях, это может быть не так полезно, если вы не будете немного болтать с ответом.

Потому что success и error устарели, так как 1.4, возможно, лучше использовать обычные методы обещания then и catch и преобразовать ответ в этих методах и вернуть обещание этого преобразованного ответа.

Я показываю тот же пример с обоими подходами и третьим промежуточным подходом:

success и error (success и error возвращают обещание ответа HTTP, поэтому нам нужна помощь $q, чтобы вернуть обещание данных):

function search(query) {
  // We make use of Angular $q library to create the deferred instance
  var deferred = $q.defer();

  $http.get('http://localhost/v1?=q' + query)
  .success(function(data,status) {
    // The promise is resolved once the HTTP call is successful.
    deferred.resolve(data);              
  })

  .error(function(reason,status) {
    // The promise is rejected if there is an error with the HTTP call.
    if(reason.error){
      deferred.reject({text:reason.error, status:status});
    }else{
      //if we don't get any answers the proxy/api will probably be down
      deferred.reject({text:'whatever', status:500});
    }
  });

  // The promise is returned to the caller
  return deferred.promise;
};

then и catch (это немного сложнее проверить из-за броска):

function search(query) {

  var promise=$http.get('http://localhost/v1?=q' + query)

  .then(function (response) {
    // The promise is resolved once the HTTP call is successful.
    return response.data;
  },function(reason) {
    // The promise is rejected if there is an error with the HTTP call.
    if(reason.statusText){
      throw reason;
    }else{
      //if we don't get any answers the proxy/api will probably be down
      throw {statusText:'Call error', status:500};
    }

  });

  return promise;
}

Существует решение на полпути (таким образом вы можете избежать throw, и в любом случае вам, вероятно, понадобится использовать $q, чтобы высмеять поведение обещаний в ваших тестах):

function search(query) {
  // We make use of Angular $q library to create the deferred instance
  var deferred = $q.defer();

  $http.get('http://localhost/v1?=q' + query)

  .then(function (response) {
    // The promise is resolved once the HTTP call is successful.
    deferred.resolve(response.data);
  },function(reason) {
    // The promise is rejected if there is an error with the HTTP call.
    if(reason.statusText){
      deferred.reject(reason);
    }else{
      //if we don't get any answers the proxy/api will probably be down
      deferred.reject({statusText:'Call error', status:500});
    }

  });

  // The promise is returned to the caller
  return deferred.promise;
}

Любые комментарии или исправления приветствуются.