在AngularJS中使用Promises的成功/错误/ finally / catch

我在AngularJs中使用$http ,我不确定如何使用返回的promise和处理错误。

我有这个代码:

 $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 }); 

这是一个好办法吗?还是有一个更简单的方法?

Promise是对语句的抽象,允许我们用asynchronous代码同步expression自己。 它们代表一次性任务的执行。

他们也提供exception处理,就像正常的代码一样,你可以从promise中返回或者抛出。

你在同步代码中想要的是:

 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. } 

promisified版本是非常相似的:

 $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's better to `return $q.reject(e)` here }).then(function(res){ // do more stuff }).catch(function(e){ // handle errors in processing or in error. }); 

忘记使用successerror方法。

这两个方法在angular度1.4都被弃用了。 基本上,贬低的原因是它们不是可链接的 ,可以这么说。

通过下面的例子,我将试图certificate我的意思是successerror是不可链接的 。 假设我们调用一个API来返回一个地址为user的对象:

用户对象:

 {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'} }); }; 

发生了什么?

因为successerror返回原来的承诺 ,即由$http.get返回的$http.get ,那么传递给该对象的callback的对象就是整个用户对象,也就是说,与前一个successcallback相同的input。

如果我们连接了两个, then这个问题就不会那么混乱了:

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

我认为以前的答案是正确的,但这里是另一个例子(只是一个fyi,成功()和错误()是根据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'); }); 

你在寻找什么types的粒度? 你通常可以通过:

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

我发现,链接多个承诺时,“终于”和“赶上”会更好。

在Angular $ http的情况下,success()和error()函数会解开响应对象,所以callback签名就像$ http(…)。success(function(data,status,headers,config))

对于then(),你可能会处理原始响应对象。 比如发布在AngularJS $ http API文档中

 $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(…)将不需要。

我喜欢布拉德利布雷斯韦特在他的博客中build议:

 app .factory('searchService', ['$q', '$http', function($q, $http) { var service = {}; service.search = function search(query) { // We make use of Angular's $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; }); }]) 

关键点:

  • parsing函数链接到我们控制器中的.then函数,即一切正常,所以我们可以信守诺言并加以解决。

  • 拒绝函数链接到我们控制器中的.catch函数,即出错了,所以我们不能遵守我们的承诺,并且需要拒绝它。

这是相当稳定和安全的,如果你有其他条件拒绝承诺,你总是可以过滤你的数据在成功函数和调用deferred.reject(anotherReason)的原因拒绝。

正如Ryan Vice在评论中所build议的那样 ,这可能不会被认为是有用的,除非你在回应中摆弄一下,可以这么说。

因为successerror从1.4开始被弃用,所以最好使用常规的承诺方法, then在这些方法中catch和转换响应,并返回转换响应的承诺。

我用两种方法和第三种方法展示了同样的例子:

successerror方法( successerror返回一个HTTP响应的承诺,所以我们需要$q的帮助来返回数据的承诺):

 function search(query) { // We make use of Angular's $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; }; 

thencatch方法(这是一个更难以testing,因为抛出):

 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; } 

虽然有一个中途的解决scheme(这样你可以避免throw ,无论如何,你可能需要使用$q来模拟testing中的承诺行为):

 function search(query) { // We make use of Angular's $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; } 

欢迎任何forms的评论或更正。