阅读 366

axios请求封装(取消重复请求)

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

对于axios的请求封装大家都不陌生,可能更多就是调用create方法创建实例和对该实例的拦截器interceptors方法进行重写。但有时候还需要在此基础上添加取消重复请求的功能,接下来来实现这个功能。

axios请求简单封装

首先对axios进行简单的封装,为了代码的简洁易懂,和拦截器相关的业务逻辑就不在这里添加。

import axios from 'axios';

const instance = axios.create({
  // ...
});

// 请求拦截器
instance.interceptors.request.use(config => {
    return config;
}, err => {
    return Promise.reject(err);
});

// 响应拦截器
instance.interceptors.response.use(res => {
    return res.data;
}, err => {
    return Promise.reject(err);
});

export default instance;
复制代码

取消重复请求

取消普通请求

首先对普通的XMLHttpRequest请求进行取消,这里用到了abort() 方法,具体代码如下:

var ajax = new XMLHttpRequest();
ajax.open('GET', '/abc');
ajax.send();
setTimeout(() => {
  ajax.abort(); // 取消当前请求
}, 300);
复制代码

取消axios请求例子

取消axios请求,这里用到了axios内部的cancelToken方法,具体代码如下:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).then(res => {});

// cancel方法里面的参数是可选参数
source.cancel('Operation canceled by the user.');
复制代码

除了上面的方法以外,axios官方还有另外一种方式:

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // 这里的c就是取消当前请求的方法,这里把c赋值给cancel变量
    cancel = c;
  })
});

// 调用取消方法
cancel();
复制代码

接下来对axios取消请求的方式就是用到上面这种,通过CancelToken构造函数上的参数来获得取消当前的请求的能力。

实现取消重复请求类

实现思路:

取消重复请求,这里主要针对的是多个重复请求发送的情况,例如在同一次操作中,发送了两个相同的请求,那么这个时候就会对前一个请求进行取消操作,真正发送出去的是后一个请求。

怎么判断请求重复了呢?这里可以通过请求的地址url,请求方式method,请求参数params|data这几个值来做唯一判断。如果两个请求上的这些信息都相同,那么就可以判断这两个请求是重复的。

请求如果重复,接下来该做什么?当然是调用cancel方法进行取消,但是怎么知道这个请求的cancel方法如何获取呢?其实这里就是怎么把当前请求和对应的cancel进行关联,这里可以用对象(Object)或者Map进行存储,把当前请求的信息作为key,对应的cancel方法作为value,存储到对象或者Map中,然后就可以根据当前请求信息或者到对应的取消方法。这里每一个请求的cancel方法需要调用new CancelToken()才可以获取。

代码实现(CancelRequest.js):

import qs from 'qs'; // 引入qs用来序列化请求参数
export default class CancelRequest {

    constructor(map) {
        this.cancelReqMap = this.isMap(map) ? map : new Map();
    }
    
    // 判断是否是map数据格式
    isMap(data) {
        if (Object.prototype.toString.call(data) === "[object Map]") return true;
    }

    // 根据请求配置信息`config`进行拼接,生成由`url、method、params、data`组成的字符串
    generateCancelReqKey(config) {
        let { url, method, params, data } = config;
        return [url, method, qs.stringify(params), qs.stringify(data)].join('&');
    }
    
    // 添加请求信息和对应的取消方法到cancelReqMap属性
    addCancelReqKey(config, cancelToken) {
        const cancelReqKey = this.generateCancelReqKey(config);
        config.cancelToken = config.cancelToken || new cancelToken((cancel) => {
            if (!this.cancelReqMap.has(cancelReqKey)) {
                this.cancelReqMap.set(cancelReqKey, cancel);
            }
        });
    }

    // 根据当前的请求信息调用对应的取消方法
    cancelReq(config) {
        const cancelReqKey = this.generateCancelReqKey(config);
        if (this.cancelReqMap.has(cancelReqKey)) {
            const cancel = this.cancelReqMap.get(cancelReqKey);
            cancel(cancelReqKey);
            this.removeRequestKey(config);
        }
    }
    
    // 删除cancelReqMap属性上的值
    removeRequestKey(config) {
        const cancelReqKey = this.generateCancelReqKey(config);
        this.cancelReqMap.delete(cancelReqKey);
    }

}
复制代码
  1. 引入qs其实是对请求参数进行序列化,qs.stringify({username: 'admin', password: '123'}) === 'username=admin&password=123'
  2. 这里使用了面向对象的方式来封装这个取消请求。因为请求的信息和对应的取消方法要存储起来,方便后面的查找,这里通过Map类型的cancelReqMap属性来存储。这里使用ES6中的Map数据结构,是因为它的键不单单局限于字符串,可以是各种数据类型,比普通的对象更加完善。
  3. generateCancelReqKey该方法是根据请求配置信息config进行拼接,生成由url、method、params、data组成的字符串。
  4. addCancelReqKey该方法主要是添加请求信息和对应的取消方法到cancelReqMap属性上。对应的取消方法是需要调用new cancelToken()才能获取到。
  5. cancelReq该方法主要是根据当前的请求信息调用对应的取消方法,最后把当前的请求信息和取消方法从cancelReqMap属性上删除。
  6. removeRequestKey根据请求配置项删除cancelReqMap上的对应的值

完善axios请求封装

import axios from 'axios';
import CancelRequest from './CancelRequest.js';

// 实例化取消请求对象
let cancelRequest = new CancelRequest();

const instance = axios.create({
  // ...
});

// 请求拦截器
instance.interceptors.request.use(config => {
  	// 检查之前是否存在相同的请求,如果存在则取消。
  	cancelRequest.cancelReq(config); 
  	// 记录当前请求
    cancelRequest.addCancelReqKey(config, axios.CancelToken); 
    return config;
}, err => {
    return Promise.reject(err);
});

// 响应拦截器
instance.interceptors.response.use(res => {
  	// 移除成功请求记录
  	cancelRequest.removeRequestKey(res.config);
    return res.data;
}, err => {
  	// 移除失败的请求记录
  	cancelRequest.removeRequestKey(err.config || {});
    if (axios.isCancel(err)) {
        console.log('重复请求信息:' + err.message);
    }
    return Promise.reject(err);
});

export default instance;
复制代码

总结

本文主要介绍了axios请求的封装和取消重复请求的功能,这里取消的请求,有可能还在请求还没有发送出去的和已经到达服务器被取消的,如果大家对请求拦截的原理感兴趣,可以阅读axios源码解读(下)

如果读者发现有不妥或者可以改善的地方,欢迎在评论区指出。如果觉得写得不错或者对你有所帮助,可以点赞、评论、转发分享,谢谢~

文章分类
前端
文章标签