无痛处理async-await的异常,告别一个请求一个try-catch----Axios示例

语言: CN / TW / HK

async-await的痛点

async-await搭档虽然也是返回Promise对象,但无法做到.then.catch那样拿到Promise中reject的结果以及其中抛出的Error。

换句话说,我们用async-await来写异步请求很多时候会这样: ts async function getUserInfo() { try { const res = await fetchUserInfo(id); } catch(err) { // 如果你用了axios拦截器,这里会是拦截器reject的结果 // 只有通过try-catch你才能拿到这个错误 } // 其他的业务代码 } 很多时候我们在axios拦截器的内部统一处理了错误,并使用一些UI框架的消息弹出功能,将错误显示给用户,这个逻辑是没问题的,但会有一些需要特殊处理的逻辑。例如加载动画的控制: ts async function getTableData() { this.loading = true; try { const res = await fetchTable({ page, size }); } catch(err) { // 如果你用了axios拦截器,这里会是拦截器reject的结果 // 只用通过try-catch你才能拿到这个错误 } finally { this.loading = false; } } 这是非常常见的业务逻辑,不管是报错还是正常,你需要在接口给你回应后将loading效果给去掉。因此这种情况是必须使用try-catch的,另外还有一些诸如: - 请求异常后回到上一页面。 - 请求异常后清空某些数据。 - 请求异常后再次发起请求。 太多了,这类情况在业务中很常见,这里只是举了几个例子。那么对于这些情况,如果使用async-await的请求风格,我们就必须每个请求都包一个try-catch,这是很麻烦的。

使用Proxy代理异步函数

我使用的axios,下面用axios来做为例子。

利用Proxy的apply陷阱拦截函数执行,并在其中统一try-catch: ```ts // 在utils文件中我们编写了一个函数。使用代理模式的思想,我们在其中统一处理了异常 export function createErrorProxy(fn: (...args: any[])=> Promise) { // 你可以在这里判断当前环境是否支持Proxy,不支持的话就把回调原封不动返回,不过这样肯定会导致一些意外 const proxyFn = new Proxy(fn, { async apply(target, context, args) { try { return await Reflect.apply(target, context, args); } catch(err) { return err; } } });

return proxyFn; } 接下来,需要做的就是重写axios的几个实例方法:ts import axios from "axios"; import { createErrorProxy } from "@/utils";

const axiosInstance = axios.create({/ config /}); // 拦截器之类的代码 // ... // 使用Proxy重写post等你用得上的请求方法 ["post", "get", "delete", "put"].forEach(type=> { axiosInstance[type] = createErrorProxy(axiosInstance[type]); });

export default axiosInstance; 到这里其实就搞定了,下面是非常常见的api封装:ts // 这块就是刚才我们处理过的axios import request from "@/request"; // api文件下的用户信息请求模块 export function fetchUserInfo(id: string): Promise { return request.get(/system/user/${id}); } // .... ``` 导入使用时就不再需要try-catch包裹了。

总结

相较于.then.catch,我更喜欢使用async-await搭档,这对基于Generator的关键字让异步代码能够以同步的编码方式书写出来,搭配解构与Error-First是我最喜欢的请求风格: ts async function getTableData() { const [error, userInfo] = await fetchUserInfo(id); if (error) { // 错误的处理 return; } // 正常情况 userModule.$patch({ userInfo }); } 你要说它比.then.catch少写了代码吗,似乎没有,这就看个人喜好了。

回到正题,经过代理后的axios请求已经统一处理了异常,后续的所有请求都不用再一次一次的重复写try-catch,我们可以放心的在await后面做一些事情。

这篇文章很短,但希望能给大家一点帮助,如果有错误或者建议欢迎各位大佬在评论区批评指正。