问题背景
在项目的model文件中,通常在effect中进行网络请求等异步操作,当网络错误或者请求结果错误时,以及代码语法错误时,无论是否主动使用throw
语句抛出错误,下一次再调用dispatch
访问effect中的函数时,均会失效。
问题复现
- 编辑完表单后通过
dispatch
访问model中effects中的edit
函数
edit
方法中进行网络请求,更新表单数据等操作
- 此时发生了网络错误,或者服务端异常,返回5xx状态的错误结果。控制台的打印结果为:
此时系统界面上的表现一切正常,加载状态正常结束,页面提示也正常,理论上等待网络或者服务器正常之后再次点击按钮,重新执行一遍流程就能返回正常结果。 - 但是,结果如下
dispatch
函数没有成功执行,甚至都没有进入到effects的edit
函数中,也没有发起网络请求,界面发生了死锁。
而且经过测试,此时访问其他的effects函数同样会失效,但是能够正常访问reducers函数。 - 总结一下,错误的发生条件:
1)effects函数中发生错误,但是没有捕获。
2)捕获错误之后,使用throw
关键字抛出。(不抛出就没问题)
原因分析
在问题复现的第三步中,有两个值得注意的输出:
- 第一个是
dispatch
的返回值,是一个pending
状态的promise对象,而且经过测试在这个dispatch
之后使用.then/.catch
都不能正常运行。而在effect函数正常执行时,这个返回的promise对象的状态是fulfilled
。
由此推测,dispatch 失效的原因应该是 umi/dva 在dispatch实现的时候做了错误的兜底捕获,并在发生这种未捕获的错误时改变自身的状态,并在下一次被调用时阻止调用。then方法无法执行的原因应该也是内部做了二次封装,返回的对象并不是一个典型的promise对象。但这些都是主观推测,等有空了研究下dispatch的源码。 - 另一个值得注意的是最后输出的一段奇怪的报错信息,明显与正常的js代码报错不同:
- 反思了下项目的设计,在发生错误或者抛出错误之后,没有做全局错误的兜底捕获,这明显是不对的,很有可能造成项目的崩溃,而且也不方便错误的定位。
解决方法
搜索上诉的报错信息发现有现成的解决方案:
umi项目中报错uncaught at _callee3 at _calle3
其他解决方案:
使用dva中函数,如果返回错误结果,再次调用,dispatch无响应
总结一下,主要有两个方案:
- 使用
try...catch...
主动捕获可能发生错误的代码,并进行手动处理,不要使用throw
再次抛出。 - 使用 dva 框架自带的
onError
全局错误管理函数,捕获错误并处理。
Dva文档中的描述:
Umi文档中的描述: