记录一下最近遇到的bug。
问题出现
//最外层
async doRequest(){
...
// 调用处
let ret = await fun(...)
...
}
// fun()内部
fun(){
...
let promises = []
for(;;){
...
promises.push(new Promise(resolve, reject){
axios.post(...).then(()=>{resolve()}).catch(()=>{reject()})
})
...
}
...
let results = await Promise.all(promises)
//遍历results,整理结果
for(;;){
if(results[i].status == 200){
...
}
else{
...
}
}
return ...
}
出现bug的时候,axios请求的后端接口直接报500错误,后端数据库报错,database error,然后整个方法好像就停在了let ret = await fun(...)
这一步,后面的代码都不再执行了。
问题分析
- promise.all没有考虑出错情况,或者是有相当出错情况,但是处理方式不对
- promise的运行方式不太了解
- 使用promise.all一次发送了太多的请求,可以和后端沟通提供批量处理的接口,将所有的参数包裹在一次请求里面
具体原理
- promise.all获得成功的结果顺序和promises数组顺序是相同的
- promise.all如果有一个回调执行失败,那么整个都会失败,并且会立即返回失败
- 在async函数内部,await后面的函数如果抛出错误,而且没有被正确的处理,那么async函数也会中止执行,等同于promise对象被reject了
- axios默认是200到300之间的返回status执行resolve,而其他的状态码执行reject
执行过程
- promise.all这里,当其中一个axios.post返回500时,执行axios的catch方法,即new Promise的reject方法,reject方法返回rejected状态的promise对象.
- new Promise返回rejected状态的promise对象,
- 错误被promise.all捕获,promise.all返回rejected,
- promise.all没有catch方法对错误进行处理,所以被async函数捕获错误,直接中止后面的代码执行
bug修复
在上面说的任意一步对抛出的异常进行处理,然后返回处理后的结果,那么后面就不会终止async函数的执行,但是在各个步骤加错误处理得到的结果可能会不太一样
- 定义axios的响应拦截器,
Axios.interceptors.response.use(response=>{}, error=>{})
.
在error处理里面修改对状态码的判断,即使是返回500也执行resolve方法,这个修改方式对现有的逻辑影响会比较大 - 在
new Promise
这里定义catch
函数,将错误处理后执行resolve结果,这个方式是比较妥当的,promise.all可以继续按原来的逻辑执行,也不会影响其他的地方 - 对promise.all方法,添加
catch
函数,将错误处理后执行resolve,这个方式虽然能够让async函数继续执行下去,但是let results = await Promise.all(promises)
这一步results就不会有任何返回了,后面的代码执行下去也是错的了,所以这样修改虽然能够保证代码执行,但是得到的是错误的结果