无痛处理async-await的异常,告别一个请求一个try-catch----Axios示例
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
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/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后面做一些事情。
这篇文章很短,但希望能给大家一点帮助,如果有错误或者建议欢迎各位大佬在评论区批评指正。