前端领域如何实现请求中断( 三 )

2、Axios
Axios想必是我们使用最多的一个第三方开源免费的HTTP库,其本身基于Promise的特性使得我们可以很方便地写出更加优雅且易维护的代码,从而避免函数多层嵌套所带来的一系列问题 。
当然,它最大的特点在于可以同时兼容浏览器端和NodeJS服务端 。底层通过判定不同的运行环境来自动提供不同的适配器,在浏览器端通过原生的XHR对象来发送请求,而在NodeJS服务端则通过内置的http模块来发送请求 。
不仅如此,在其底层的Promise管道链中还为我们暴露了称之为拦截器的入口,使得我们可以参与到一个请求的生命周期中,在请求发送之前和响应接收之后能够自定义实现数据的装配和转换操作 。带来的如此之多的人性化操作,使得我们没有理由不去用它,这也奠定了其长久以来依旧如此火爆的基础 。
言归正传,在Axios中同样为我们提供了请求中断的相关API 。首先抛出一个基础示例:
复制// 安装 axiosnpm install --save axios// 导入 axiosimport axios from 'axios';// 创建 axios 实例const instance = axios.create({baseURL: 'https://www.some-domain.com/path/to/example',timeout: 30000,headers: {'Content-Type': 'application/x-www-form-urlencoded',},});// 设置 axios 实例默认配置instance.defaults.headers.common['Authorization'] = '';instance.defaults.headers.post['Content-Type'] = 'application/json; charset=UTF-8';// 自定义请求拦截器instance.interceptors.request.use(config => {const token = window.localStorage.getItem('token');token && (config.headers['Authorization'] = token);return config;}, error => Promise.reject(error));// 自定义响应拦截器instance.interceptors.response.use(response => {if (response.status === 200) {return Promise.resolve(response.data);}return Promise.reject(response);}, error => Promise.reject(error));1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.接下来我们结合Axios提供的CancelToken构造函数来创建一个简单的post请求:
复制const CancelToken = axios.CancelToken;let cancel;instance.post('/api/user/123', {name: 'new name',phone: 'new phone',}, {// CancelToken 构造函数接收一个 executor 函数参数,并且该函数接收一个取消函数 c 用于取消该次请求cancelToken: new CancelToken(function executor(c) {// 将取消函数赋值到外部变量,方便从外部取消请求cancel = c;}),});// 手动取消请求cancel();1.2.3.4.5.6.7.8.9.10.11.12.13.14.针对需要同时取消多个请求以及自动取消的应用场景,上面的示例显然不能满足我们的需求 。这里我们同样可以利用上一小节的思路来维护一个请求接口地址以及请求体和取消函数c之间的映射关系 。
同时为了避免在每个请求中都需要手动去实例化CancelToken,我们可以巧妙利用request拦截器来整合这部分的逻辑,实现逻辑复用 。首先我们将缓存逻辑拆分到一个单独的文件中:
复制// cacheUtils.jsexport const CacheUtils = {// 存储请求接口地址以及请求体和取消函数之间的映射关系cache: {},// 根据提供的键名 key 取消对应的请求,若未提供则取消全部请求clearCache: function (key) {if (key) {const cancel = this.cache[key];if (cancel && typeof cancel === 'function') {cancel();delete this.cache[key];}return;}Object.keys(this.cache).forEach(cacheKey => {const cancel = this.cache[cacheKey];cancel();delete this.cache[cacheKey];});},};1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.接下来我们将其应用到请求拦截器和响应拦截器中:
复制import qs from 'qs';import { CacheUtils } from './cacheUtils.js';// 自定义请求拦截器instance.interceptors.request.use(config => {let cacheKey = config.url;const token = window.localStorage.getItem('token');token && (config.headers['Authorization'] = token);const method = config.method.toLowerCase();if (method === 'get' && config.params && typeof config.params === 'object') {cacheKey += qs.stringify(config.params, { addQueryPrefix: true });}if (['post', 'put', 'patch'].includes(method) && config.data && typeof config.data =https://www.isolves.com/it/cxkf/qd/2022-01-29/== 'object') {config.data = qs.stringify(config.data);cacheKey += `_${qs.stringify(config.data, { arrayFormat: 'brackets' })}`;}// 每次发送请求之前将上一个未完成的相同请求进行中断CacheUtils.cache[cacheKey] && CacheUtils.clearCache(cacheKey);// 将当前请求所对应的取消函数存入缓存config.cancelToken = new axios.CancelToken(function executor(c) {CacheUtils.cache[cacheKey] = c;});// 临时保存 cacheKey,用于在响应拦截器中清除缓存config.cacheKey = cacheKey;return config;}, error => Promise.reject(error));// 自定义响应拦截器instance.interceptors.response.use(response => {// 响应接收之后清除缓存const cacheKey = response.config.cacheKey;delete CacheUtils.cache[cacheKey];if (response.status === 200) {return Promise.resolve(response.data);}return Promise.reject(response);}, error => {// 响应异常清除缓存if (error.config) {const cacheKey = error.config.cacheKey;delete CacheUtils.cache[cacheKey];}return Promise.reject(error);});1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.


推荐阅读