前言
此文章是以前端使用的角度去看待 try catch 这回事,如果有不同意见可以理性讨论。相信大部分人都会在请求接口的时候使用 try catch ,在 catch 的时候做一些错误消息提示或代码处理,但是这个过程是否可以统一处理?下面我会以我这边的业务出发去考虑这个问题。
业务代码
这个是 axios 的拦截器,接口返回的时候判断 code 是不是一个正常值,是的话返回数据,不是的话抛出一个 promise 错误。
axios.interceptors.response.use(
response => {
const { code, data } = response.data || {}
if (code === CODE_OK) {
return data
}
return Promise.reject(response.data)
}
)
这是一个登陆请求的代码,要求接口报错的时候要把错误信息提示出来(大部分请求都是需要将错误提示 toast 出来)
async handleLogin() {
try {
await login(this.model)
} catch (error) {
this.$message.error(error.message)
}
}
存在问题:
catch的内容如果每个请求都需要做一遍,这个代码其实重复的catch处理了提示之后错误无法在控制台看到,其他错误难追溯
解决问题
如果要移除 try catch ,那么 catch 的内容我们就需要找另一个地方统一去做。
那么 axios 的拦截器可不可以呢?
axios.interceptors.response.use(
response => {
const { code, data, message } = response.data || {}
if (code === CODE_OK) {
return data
}
if (message) {
Message.error(message)
}
return Promise.reject(response.data)
}
)
上面这段代码确实是可以将请求的错误统一提示,也能在控制台看到对应的 error 信息,但是假如某个业务不需要出提示又或者它的提示需要做一些定制,那上面这样的代码就不够灵活了。
一番思考后
我觉得我们还是需要延用 try catch 的一些想法,当遇到 error 的时候才做出处理,没有 error 的时候不做处理。当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件。因此我们可以通过监听 unhandledrejection 事件来统一处理请求错误,当然还需要区分请求的 promise 错误和 其他的 promise 错误,可以通过 error 的内容区分也可以通过新加一个请求的错误类型在 axios 拦截器的时候使用,这里不过多展示。
mounted() {
window.addEventListener('unhandledrejection', this.onPromiseError)
},
methods: {
onPromiseError(event) {
const { code, message } = event.reason || {}
if (code && message) {
this.$message.error(message)
}
}
}
如果你使用了 vue ,还需要在 errorHandler 里面同样处理一下。因为 vue 在很多地方都做了错误捕获,然后在相关的钩子里面处理错误。promise 错误被捕获后不会再触发 unhandledrejection 事件。对 vue 错误捕获感兴趣的话可以看下 vue 2.+ 源码里面的 invokeWithErrorHandling方法。
另外,如果你不需要出提示或者要做其他定制内容,请在对应的业务代码里面使用 try catch 处理你的 error ,这样就不会被统一处理。
优化
建议这种错误可以定义一个错误类型的 class ,这样可以和其他的 error 区别开来,比较好处理。