你也许不需要try/catch

267 阅读1分钟

前言

此文章是以前端使用的角度去看待 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)
  }
}

存在问题:

  1. catch 的内容如果每个请求都需要做一遍,这个代码其实重复的
  2. 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 信息,但是假如某个业务不需要出提示又或者它的提示需要做一些定制,那上面这样的代码就不够灵活了。

image.png

一番思考后

我觉得我们还是需要延用 try catch 的一些想法,当遇到 error 的时候才做出处理,没有 error 的时候不做处理。当 Promisereject 且没有 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 区别开来,比较好处理。