dva2 onError 处理的异常仍然抛出

Created on 15 Sep 2017  ·  11Comments  ·  Source: dvajs/dva

onError 捕获异常后, 异常仍然被抛出

正常情况下在 effect 处理时发生异常, 会被 dva 注册的 onError 捕获到, 在此可以做通用处理, 非常好用.

但是升级 dva2 之后发现一个现象(dva1 时没有出现, 或者是其他情况当时没有注意), 异常触发时会进 onError, 但是异常一样会被正常抛出, 导致 console 输出 error

我并不是专职前端, 以上情况不知道是不是刻意为之, 如果是烦请指导一下有没有什么好辙, 能够阻止异常在 onError 处理的同时继续被抛出

Code to reproduce the issue: (请提供可复现的代码或者步骤)

dva onError:
```java script
onError: (e) => {
console.log('onError', e)
}

any effect: 
```java script
() => { throw Error('any error')}

Expected behavior: (预期的正常效果)

默认捕获后静默处理, 不再抛出, 如果需要再抛出异常, 可在 onError 自行 throw

Actual behavior: (实际效果)

console print:
onError Error: any error ...

and console error:
Uncaught (in promise) Error: any error

Versions of packages used: (哪个库的哪个版本出现的问题)

dva 2.0.1

enhancement faq

Most helpful comment

return true 和 false 感觉也有点奇怪。这样吧,提供 preventDefault 方法,执行后就阻止后续的 reject 操作。

onError(e) {
  e.preventDefault();
}

All 11 comments

排查了下。

原因是在 dva@2 中,如果 dispatch 的 action 是一个 Effect,会返回 Promise。这样就可以在 Component 中处理 dispatch 的回调 ( #175 ),而由于 Component 里会需要处理 Promise 的 reject 场景,所以这里的错误不能被 onError 吞掉。。

dispatch({
  type: 'effectAction'
})
  .then(() => {})
  .catch(() => {})

然后未捕获的 Promise Rejection 就通过 console.error 输出,但并不是抛错,所以也不会被 window.onerror 捕获到,对用户无影响。

如果不想看到这行错误,有两个办法:

1> 给 effect 的 dispatch().catch

dispatch().catch(err => {});

2> 全局监听 unhandledrejection 事件,并阻止通过 console.error 打印出。

window.addEventListener("unhandledrejection", function (event) {
  event.preventDefault();
});

多谢回复, 对用户影响倒是不大, 主要是开发过程中有很多比较蛋疼的问题, 比如 4xx 的 http status code, 都会打印异常信息, 另外我们在 react native 的场景下使用, 开发模式下 console.error 实际上会弹一个 yellow box...

至于这两种处理方法:

  1. catch 意味着所有可能异常的地方都需要特殊处理, 或者主动调一个通用的 onError, 就需要点重复代码, 不那么通用了的感觉
  2. rn 下不确定有没有类似 event 能够吞掉异常

大胆的提出一个不成熟的想法, 在 onError 传入 resolvereject 对象, 可在 onError 内部决定如何处理, 或者通过 onError 的返回值决定如何处理, 不知是否有操作空间?

onError 里做不了,Promise 和 saga 里的时机不同,会导致 onError 触发两次。增加一个 onDispatchError 你觉得如何?

回复好快啊, 确实如果在 onError 里面 reject 会再次进 onError...
如果有 onDispatchError, 只是用来处理 actionerror, 想来确实可行, 如果需要抛出就 reject, 再次进入自己写的 catch 或者 onError
也就是说, 那么此类 error 只进 onDispatchError, 而不会进两个 error handler 对吧?

  function *sagaWithCatch(...args) {
    try {
      yield sagaEffects.put({ type: `${key}${NAMESPACE_SEP}@@start` });
      const ret = yield effect(...args.concat(createEffects(model)));
      yield sagaEffects.put({ type: `${key}${NAMESPACE_SEP}@@end` });
      resolve(key, ret);
    } catch (e) {
      onError(e);
      reject(key, e);
    }
  }

问题是这段代码吧?我和楼主一起查的这个问题,个人觉得有两种方式可以考虑。

  // 传 reject 进 onError
  function *sagaWithCatch(...args) {
    try {
      yield sagaEffects.put({ type: `${key}${NAMESPACE_SEP}@@start` });
      const ret = yield effect(...args.concat(createEffects(model)));
      yield sagaEffects.put({ type: `${key}${NAMESPACE_SEP}@@end` });
      resolve(key, ret);
    } catch (e) {
      onError(e, reject.bind(null, key, e)); // 把 redux middleware 产生的 promise 的 reject 给 onError ,由 onError 决定要不要触发这个 promise 的 catch 链
      // 甚至可以把 resolve 都传进去,可以做到部分 error 处理后仍然正常执行 dispatch 的 then ,但感觉太奇怪了
    }
  }

  // onError 返回一个 true/false
  function *sagaWithCatch(...args) {
    try {
      yield sagaEffects.put({ type: `${key}${NAMESPACE_SEP}@@start` });
      const ret = yield effect(...args.concat(createEffects(model)));
      yield sagaEffects.put({ type: `${key}${NAMESPACE_SEP}@@end` });
      resolve(key, ret);
    } catch (e) {
      const handled = onError(e);
      if (!handled) reject(key, e); // onError 返回是否要继续抛出 error
    }
  }

return true 和 false 感觉也有点奇怪。这样吧,提供 preventDefault 方法,执行后就阻止后续的 reject 操作。

onError(e) {
  e.preventDefault();
}

@sorrycc 👍 嗯 确实这样更合理,毕竟 onError 语义上不太应该返回一个值,而且返回值的 true / false 具体意义也不明。

坐等 release 😄

@sorrycc 作者您好,有一个小问题,我比较有困惑,我在request.js中统一处理,状态码不是2xx,3xx的统一进行reject操作,并且在dispatch中指定了catch,但是并没有在effect中使用try catch去捕获异常,我认为异常流程会是 先通过dispatch 的 catch,当我没有catch的时候在走到onError。但是现在直接走到onError里面去了,我目前知道的是可以在effect里面去try catch,并且在catch中执行reject操作可以解决我的需求,但是每个effect感觉很怪,这种行为是否符合你的期望?如果是的,那么理由是什么呢?

@jinyang1994 现在有解决了嘛

@jinyang1994 @dyf19118

原因是在 dva@2 中,如果 dispatch 的 action 是一个 Effect,会返回 Promise。这样就可以在 Component 中处理 dispatch 的回调 ( #175 ),而由于 Component 里会需要处理 Promise 的 reject 场景,所以这里的错误不能被 onError 吞掉。。

参考上面说的,如果 effects 中抛异常没有被捕获,会执行 onError,然后才是 dispatch 返回的 Promise 处理。如果在 onError 中调用 err. preventDefault() 则后续 dispatch 的 catch 不会执行。

dispatch一个effect,里面的业务代码出现了问题,我没有在effect里面try catch,结果页面直接崩溃了,想通过一个顶层的全局方便来捕捉错误,防止页面崩溃。

  • 现在我加了全局捕获错误:如下
window.addEventListener("unhandledrejection", function (event) {
 console.log('err.....,,,,', event)
  event.preventDefault();
});

错误是捕获到了,但是页面还是崩溃了,我该怎么防止页面崩溃呢

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zhuangtq picture zhuangtq  ·  3Comments

not3 picture not3  ·  3Comments

itiwll picture itiwll  ·  4Comments

sorrycc picture sorrycc  ·  3Comments

mclouvem picture mclouvem  ·  4Comments