AngularJS - 承诺重新抛出异常

Vit*_*lyB 17 javascript promise angularjs angular-promise

在以下代码中,$ q promise 的catch函数捕获异常:

// Fiddle - http://jsfiddle.net/EFpn8/6/
f1().then(function(data) {
        console.log("success 1: "+data)
        return f2();
    })
    .then(function(data) {console.log("success 2: "+data)})
    .catch(function(data) {console.log("error: "+data)});

function f1() {
    var deferred = $q.defer();
    // An exception thrown here is not caught in catch
    // throw "err";
    deferred.resolve("done f1");        
    return deferred.promise;
}

function f2() {
    var deferred = $q.defer();
    // An exception thrown here is handled properly
    throw "err";
    deferred.resolve("done f2");        
    return deferred.promise;
}  
Run Code Online (Sandbox Code Playgroud)

但是,当我查看控制台日志输出时,我看到以下内容:

在此输入图像描述

该异常是在Angular中捕获的,但也被浏览器的错误处理所捕获.此行为确实与Q库重现.

这是一个错误吗?我怎样才能真正捕获$ q的异常?

Ben*_*aum 16

Angular $q使用一种约定,无论被捕获,都会记录抛出的错误.相反,如果你想表示拒绝,你需要return $q.reject(...这样:

function f2() {
    var deferred = $q.defer();
    // An exception thrown here is handled properly
    return $q.reject(new Error("err"));//throw "err";
    deferred.resolve("done f2");        
    return deferred.promise;
}  
Run Code Online (Sandbox Code Playgroud)

这是为了区分拒绝与SyntaxError之类的错误.就个人而言,这是一个我不同意的设计选择,但它是可以理解的,因为$q它很小,所以你无法真正构建一个可靠的未处理的拒绝检测机制.在像Bluebird这样强大的图书馆中,不需要这种东西.

作为旁注 - 永远不要抛出字符串:你会错过堆栈跟踪.

  • @Sebastian我更喜欢人们不复制/粘贴代码我在这里盲目地发布并实际尝试阅读和理解它然后应用我自己讨论的原则. (6认同)
  • 你是认真的吗?从未调用返回新拒绝的promise后的代码.并且您不需要延迟,因为您使用$ q.reject()创建新的承诺... (3认同)

Mic*_*mza 6

这是一个错误吗?

不.查看$ q源代码可以看出,创建了一个故意的try/catch块来响应回调中抛出的异常.

  1. 通过你的召唤拒绝承诺 deferred.reject
  2. 调用注册的Angular异常处理程序.从$ exceptionHandler文档中可以看出,默认行为是将其作为错误记录到浏览器控制台,这是您所观察到的.

...也被浏览器的错误处理所捕获

为了澄清,该异常不是由浏览器直接处理,而是因为Angular已调用而显示为错误 console.error

我怎样才能真正捕获$ q的异常?

回调在一段时间后执行,当前调用堆栈已清除,因此您将无法将外部函数包装在try/ catchblock中.但是,您有两个选择:

  • 在回调中放入try/ catch阻止可能抛出异常的代码:

    f1().then(function(data) {
      try {
        return f2();
      } catch(e) {
        // Might want convert exception to rejected promise
        return $q.reject(e);
      }
    })
    
    Run Code Online (Sandbox Code Playgroud)
  • 更改Angular $exceptionHandler服务的行为方式,例如如何覆盖$ exceptionHandler实现.您可能会将其更改为绝对没有任何内容,因此控制台的错误日志中永远不会有任何内容,但我认为我不会建议这样做.


geo*_*awg 5

使用AngularJS 1.6版修复

这种行为的原因是未被​​捕获的错误与常规拒绝不同,例如,它可能由编程错误引起.实际上,这对用户来说是混乱或不受欢迎的,因为本机承诺或任何其他流行的承诺库都不会将抛出的错误与常规拒绝区分开来.(注意:虽然这种行为不符合Promises/A +规范,但也没有规定.)

$ Q:

由于e13eea,从promise onFulfilledonRejection处理程序抛出的错误被视为与常规拒绝完全相同.以前,它也会被传递给$exceptionHandler()(除了以错误为理由拒绝承诺).

新行为适用于依赖的所有服务/控制器/过滤器等$q(包括内置服务,如$http$route).例如,$http's transformRequest/Response函数或路由的redirectTo函数以及路由的resolve对象中指定的函数将不再导致调用$exceptionHandler()它们是否抛出错误.除此之外,一切都会继续以同样的方式行事; 即承诺将被拒绝,路线转换将被取消,$routeChangeError事件将被广播等.

- AngularJS开发人员指南 - 从V1.5迁移到V1.6 - $ q