简体   繁体   English

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

[英]Using success/error/finally/catch with Promises in AngularJS

I'm using $http in AngularJs, and I'm not sure on how to use the returned promise and to handle errors. 我在AngularJs中使用$http ,我不确定如何使用返回的promise并处理错误。

I have this code: 我有这个代码:

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

Is this a good way to do it, or is there an easier way? 这是一个很好的方法吗,还是有更简单的方法?

Promises are an abstraction over statements that allow us to express ourselves synchronously with asynchronous code. Promise是对语句的抽象,允许我们与异步代码同步表达自己。 They represent a execution of a one time task. 它们代表一次性任务的执行。

They also provide exception handling, just like normal code, you can return from a promise or you can throw. 它们还提供异常处理,就像普通代码一样,您可以从承诺返回,也可以抛出。

What you'd want in synchronous code is: 您在同步代码中想要的是:

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

The promisified version is very similar: 宣传的版本非常相似:

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

Forget about using success and error method. 忘记使用successerror方法。

Both methods have been deprecated in angular 1.4. 两种方法都已在角度1.4中弃用。 Basically, the reason behind the deprecation is that they are not chainable-friendly , so to speak. 基本上,弃用背后的原因是它们不是可链接友好的 ,可以这么说。

With the following example, I'll try to demonstrate what I mean about success and error being not chainable-friendly . 通过以下示例,我将尝试演示我对success以及error是不可链接友好的 Suppose we call an API that returns a user object with an address: 假设我们调用一个返回带有地址的用户对象的API:

User object: 用户对象:

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

Call to the API: 致电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'}
    });
};

What happened? 发生了什么?

Because success and error return the original promise , ie the one returned by $http.get , the object passed to the callback of the then is the whole user object, that is to say the same input to the preceding success callback. 因为successerror恢复原来承诺 ,即通过返回一个$http.get ,传递给的回调的对象then是整个用户对象,也就是说输入前面一样success的回调。

If we had chained two then , this would have been less confusing: 如果我们链接2 then ,这将是减少混乱:

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

I think the previous answers are correct, but here is another example (just a fyi, success() and error() are deprecated according to AngularJS Main page : 我认为以前的答案是正确的,但这是另一个例子(根据AngularJS 主页 ,只是fyi,success()和error()不推荐使用:

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

What type of granularity are you looking for? 您在寻找什么类型的粒度? You can typically get by with: 您通常可以通过:

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

I've found that "finally" and "catch" are better off when chaining multiple promises. 我发现链接多个承诺时“终于”和“抓住”会更好。

In Angular $http case, the success() and error() function will have response object been unwrapped, so the callback signature would be like $http(...).success(function(data, status, headers, config)) 在Angular $ http的情况下,success()和error()函数会将响应对象解包,因此回调签名就像$ http(...)。success(function(data,status,headers,config))

for then(), you probably will deal with the raw response object. 对于then(),您可能会处理原始响应对象。 such as posted in AngularJS $http API document 比如发布在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;
    });

The last .catch(...) will not need unless there is new error throw out in previous promise chain. 除非在先前的promise链中抛出新的错误,否则最后的.catch(...)将不需要。

I do it like Bradley Braithwaite suggests in his blog : 我这样做就像Bradley Braithwaite在他的博客中建议的那样:

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

Key Points: 关键点:

  • The resolve function links to the .then function in our controller ie all is well, so we can keep our promise and resolve it. resolve函数链接到我们控制器中的.then函数,即一切都很好,所以我们可以信守承诺并解决它。

  • The reject function links to the .catch function in our controller ie something went wrong, so we can't keep our promise and need to reject it. 拒绝功能链接到我们控制器中的.catch函数,即出错了,所以我们无法履行承诺,需要拒绝它。

It is quite stable and safe and if you have other conditions to reject the promise you can always filter your data in the success function and call deferred.reject(anotherReason) with the reason of the rejection. 它非常稳定和安全,如果您有其他条件拒绝承诺,您可以随时在成功函数中过滤数据并使用拒绝原因调用deferred.reject(anotherReason)

As Ryan Vice suggested in the comments , this may not be seen as useful unless you fiddle a bit with the response, so to speak. 正如Ryan Vice在评论中所建议的那样 ,这可能不会被视为有用,除非你对答案略有提及 ,可以这么说。

Because success and error are deprecated since 1.4 maybe it is better to use the regular promise methods then and catch and transform the response within those methods and return the promise of that transformed response. 因为自1.4以来不推荐使用successerror ,所以最好使用常规的promise方法then catch并转换这些方法中的响应并返回转换后的响应的承诺。

I am showing the same example with both approaches and a third in-between approach: 我用两种方法和第三种中间方法展示了相同的例子:

success and error approach ( success and error return a promise of an HTTP response, so we need the help of $q to return a promise of data): 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;
};

then and catch approach (this is a bit more difficult to test, because of the throw): 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;
}

There is a halfway solution though (this way you can avoid the throw and anyway you'll probably need to use $q to mock the promise behavior in your tests): 虽然有一个中途解决方案(这样你可以避免throw ,无论如何你可能需要使用$q来模拟测试中的promise行为):

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

Any kind of comments or corrections are welcome. 欢迎任何形式的评论或更正。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM