在AngularJS中将成功/错误/最终/捕获与承诺一起使用


112

$http在AngularJs中使用,我不确定如何使用返回的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
    });

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

Answers:


103

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

它们还提供异常处理,就像普通代码一样,您可以从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.
}

承诺的版本非常相似:

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

4
您将如何使用success()error()finally()与之结合catch()?还是我必须使用then(successFunction, errorFunction).catch(exceotionHandling).then(cleanUp);
Joel 2014年

3
@Joel一般,你不希望永远使用successerror(更喜欢.then.catch相反,你可以(也应该)省略了errorFunction.then使用交流catch在我上面的代码等)。
本杰明·格伦鲍姆

@BenjaminGruenbaum您能详细说明为什么建议避免success/ error吗?当Eclipse看到时,我的Eclipse也会运行amok .catch(,所以我["catch"](现在使用。如何驯服Eclipse?
吉斯莫

Angular的$ q库的$ http模块实现使用.success和.error而不是.then和.catch。但是,在我的测试中,使用.then和.catch Promise时,我可以访问$ http Promise的所有属性。另请参阅zd333的答案。
史蒂夫·K

3
@SirBenBenji $ Q没有.success.error,$ HTTP返回$ Q承诺在加入了中successerror处理程序-然而,这些处理程序不链,一般应是否/何时可能避免。通常,如果您有问题,最好将其作为新问题而不是对旧问题进行评论。
本杰明·格林鲍姆

43

忘记使用successerror方法。

两种方法均已在角度1.4中弃用。基本上,弃用的原因是它们不是可链接友好的

在下面的示例中,我将尝试证明我的意思successerror并且不是可链接友好的。假设我们调用一个API,该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'}
    });
};

发生了什么?

因为successerror返回了原始的诺言,即返回的诺言$http.get传递给的回调的对象then是整个用户对象,也就是说,与前一个success回调的输入相同。

如果我们将两个链接在一起then,这将减少混乱:

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

1
同样值得注意的是successerror它们仅添加到$http调用的立即返回中(而不是原型),因此,如果您在它们之间调用另一个promise方法(例如,通常调用return $http.get(url)包装在基库中,但稍后决定切换微调器)使用return $http.get(url).finally(...))进行库调用,那么您将不再拥有这些便捷方法。
drzaus

35

我认为前面的答案是正确的,但是这是另一个示例(根据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');
    });

3
据我所知,最后没有返回答复。
diplosaurus

11

您正在寻找哪种类型的粒度?通常,您可以通过以下方式获得帮助:

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

我发现链接多个promise时,“ finally”和“ catch”效果更好。


1
在您的示例中,错误处理程序仅处理$ http错误。
本杰明·格林鲍姆

1
是的,我仍然需要处理成功/错误功能中的异常。然后,我需要某种通用处理程序(可以在其中设置类似内容loading = false
Joel 2014年

1
您可以用大括号(而不是括号)来关闭then()调用。
保罗·麦克林

1
.catch()
不适

这是处理HTTP错误的正确答案返回给控制器
莱昂

5

在Angular $ http的情况下,success()和error()函数将打开响应对象,因此回调签名将类似于$ 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;
    });

除非先前的promise链中抛出新的错误,否则不需要最后一个.catch(...)。


2
不建议使用成功/错误方法。
OverMars

-3

我做到了,就像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;
            });
    }])

关键点:

  • resolve函数链接到控制器中的.then函数,也就是说一切都很好,因此我们可以兑现诺言并加以解决。

  • 拒绝函数链接到控制器中的.catch函数,即出现问题,因此我们无法兑现承诺,需要拒绝它。

这是非常稳定和安全的,如果您有其他条件拒绝承诺,则可以始终在成功函数中过滤数据并deferred.reject(anotherReason)以拒绝原因进行调用。

正如Ryan Vice在评论中所建议的那样,除非您对响应进行一些摆弄,否则这可能不会被视为有用。

因为从1.4开始不推荐使用successerror,所以最好使用常规的promise方法thencatch在这些方法中转换响应并返回转换后的响应的promise。

我在两种方法和第三种中间方法中都显示了相同的示例:

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方法(由于抛出,这很难测试):

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

但是,有一个中途解决方案(通过这种方式,您可以避免使用throwand,并且您可能需要使用它$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;
}

欢迎提出任何意见或更正。


2
为什么要使用$ q将诺言包装在诺言中。为什么不只返回$ http.get()返回的promise?
Ryan Vice

因为success()并且error()不会像这样返回新的承诺then()。有了$q我们,我们的工厂将返回数据承诺而不是HTTP响应承诺。
制表师

您的回应让我感到困惑,所以也许我不能很好地解释自己。除非您要操纵响应,否则您可以简单地返回$ http返回的诺言。看到我刚刚写的这个例子:jsbin.com/belagan/edit?html,js,output
Ryan Vice

1
我看不到价值。对我来说,这是不必要的,我拒绝使用这种方法的项目的代码审查,但是,如果您从中受益,那么您应该使用它。在最佳的有角度的最佳实践文章中,我也看到了一些希望,这些文章将不必要的包装作为气味。
Ryan Vice

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.