正常情况下在 effect 处理时发生异常, 会被 dva 注册的 onError 捕获到, 在此可以做通用处理, 非常好用.
但是升级 dva2 之后发现一个现象(dva1 时没有出现, 或者是其他情况当时没有注意), 异常触发时会进 onError, 但是异常一样会被正常抛出, 导致 console 输出 error
我并不是专职前端, 以上情况不知道是不是刻意为之, 如果是烦请指导一下有没有什么好辙, 能够阻止异常在 onError 处理的同时继续被抛出
dva onError:
```java script
onError: (e) => {
console.log('onError', e)
}
any effect:
```java script
() => { throw Error('any error')}
默认捕获后静默处理, 不再抛出, 如果需要再抛出异常, 可在 onError 自行 throw
console print:
onError Error: any error ...
and console error:
Uncaught (in promise) Error: any error
dva 2.0.1
排查了下。
原因是在 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...
至于这两种处理方法:
catch
意味着所有可能异常的地方都需要特殊处理, 或者主动调一个通用的 onError
, 就需要点重复代码, 不那么通用了的感觉event
能够吞掉异常大胆的提出一个不成熟的想法, 在 onError
传入 resolve
和 reject
对象, 可在 onError
内部决定如何处理, 或者通过 onError
的返回值决定如何处理, 不知是否有操作空间?
放 onError
里做不了,Promise 和 saga 里的时机不同,会导致 onError
触发两次。增加一个 onDispatchError
你觉得如何?
回复好快啊, 确实如果在 onError
里面 reject
会再次进 onError
...
如果有 onDispatchError
, 只是用来处理 action
的 error
, 想来确实可行, 如果需要抛出就 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();
});
错误是捕获到了,但是页面还是崩溃了,我该怎么防止页面崩溃呢
Most helpful comment
return true 和 false 感觉也有点奇怪。这样吧,提供 preventDefault 方法,执行后就阻止后续的 reject 操作。