likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 76

async-await的痛点

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

换句话说,我们用async-await来写异步请求很多时候会这样:

async function getUserInfo() {
  try {
    const res = await fetchUserInfo(id);
  } catch(err) {
    // 如果你用了axios拦截器,这里会是拦截器reject的结果
    // 只有通过try-catch你才能拿到这个错误
  }
  // 其他的业务代码
}

很多时候我们在axios拦截器的内部统一处理了错误,并使用一些UI框架的消息弹出功能,将错误显示给用户,这个逻辑是没问题的,但会有一些需要特殊处理的逻辑。例如加载动画的控制:

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:

// 在utils文件中我们编写了一个函数。使用代理模式的思想,我们在其中统一处理了异常
export function createErrorProxy<T = any>(fn: (...args: any[])=> Promise<T>) {
  // 你可以在这里判断当前环境是否支持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的几个实例方法:

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封装:

// 这块就是刚才我们处理过的axios
import request from "@/request";
// api文件下的用户信息请求模块
export function fetchUserInfo(id: string): Promise<YourResponseType> {
    return request.get(`/system/user/${id}`);
}
// ....

导入使用时就不再需要try-catch包裹了。

总结

相较于.then.catch,我更喜欢使用async-await搭档,这对基于Generator的关键字让异步代码能够以同步的编码方式书写出来,搭配解构与Error-First是我最喜欢的请求风格:

async function getTableData() {
    const [error, userInfo] = await fetchUserInfo(id);
    if (error) {
        // 错误的处理
        return;
    }
    // 正常情况
    userModule.$patch({ userInfo });
}

你要说它比.then.catch少写了代码吗,似乎没有,这就看个人喜好了。

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

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

转载自:https://juejin.cn/post/7090407348673445902
评论
请登录