美文网首页
axios取消重复请求实现

axios取消重复请求实现

作者: 桃花谷主V | 来源:发表于2021-11-26 15:15 被阅读0次

    在我们日常开发中,有这样一种场景必须要进行处理,那就是在提交表单的时候,如果很快的重复点击两次,会造成重复请求,第二次请求就会报错,给用户带来很不好的体验,同时如果后端没有加以控制,也容易造成数据重复。所以我们需要对这种重复请求进行处理和控制。

    axios是现在前端项目开发中必用的一个用于前端后网络请求的工具,它是基于ajaxpromise封装而成,很受欢迎。

    那么,今天就结合axios来实现一下如何取消重复请求。

    思路整理

    取消重复请求的思路就是,将每一次请求的urlmethodparamsdata拼接起来组成一个key,然后添加到map中,下一次请求时就拿keymap中查找是否已存在,如果存在就表示重复请求,就取消,如果不存在就放行,等请求成功后在从map中删除这个key

    axios中如何取消请求

    • 普通请求取消
      在普通的ajax中,是通过XMLHttpRequestabort()方法实现取消请求的。
    var ajax = new XMLHttpRequest();
    ajax.open('GET', '/abc');
    ajax.send();
    setTimeout(() => {
      ajax.abort(); // 取消当前请求
    }, 300);
    
    • axios请求取消
      axios中是通过CancelToken给每个请求添加一个cancelToken属性,使得每个请求具备取消请求的能力。有两种方法实现:
    // 方式一:
    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();
    
    axios.get('/user/12345', {
      cancelToken: source.token
    }).then(res => {});
    
    // cancel方法里面的参数是可选参数
    source.cancel('取消请求');
    
    // 方式二:
    const CancelToken = axios.CancelToken;
    let cancel;
    
    axios.get('/user/12345', {
      cancelToken: new CancelToken(function executor(c) {
        // 这里的c就是取消当前请求的方法,这里把c赋值给cancel变量
        cancel = c;
      })
    });
    // 调用取消方法
    cancel();
    

    两种实践方式

    • 重复请求取消前一次
    1. 封装取消重复请求类
    // cancel-request.js
    import qs from 'qs'
    export default class CancelRequest {
      constructor() {
        this.pendingRequest = new Map()
      }
      // 根据请求信息生成唯一标识key
      geterateReqKey(config) {
        const { url, method, params, data } = config
        return [url, method, qs.stringify(params), qs.stringify(data)].join('&')
      }
      // 把当前请求信息添加到pendingRequest对象中
      addPendingRequest(config, CancelToken) {
        const requestKey = this.geterateReqKey(config)
        config.cancelToken =
          config.cancelToken ||
          new CancelToken(cancel => {
            if (!this.pendingRequest.has(requestKey)) {
              // 把请求取消方法作为 map 值存起来
              this.pendingRequest.set(requestKey, cancel)
            }
          })
      }
      // 检查是否存在重复请求,若存在则取消前一次请求
      removePendingRequest(config) {
        const requestKey = this.geterateReqKey(config)
        if (this.pendingRequest.has(requestKey)) {
          const cancel = this.pendingRequest.get(requestKey)
          // 取消请求
          cancel(requestKey)
          // 删除map中对应的属性
          this.removeRequestKey(config)
        }
      }
      // 从pendingRequest中删除对应的key
      removeRequestKey(config) {
        const requestKey = this.geterateReqKey(config)
        this.pendingRequest.delete(requestKey)
      }
    }
    
    1. axios拦截中使用
    // request.js
    import axios from 'axios';
    import CancelRequest from './cancel-request.js'
    // 实例化
    let cancelRequest = new CancelRequest()
    const instance = axios.create({
      // ...
    });
    
    // 请求拦截器
    instance.interceptors.request.use(config => {
      // 在请求开始之前检查先前的请求,如果是重复请求,删除之前的
      cancelRequest.removePendingRequest(config);
      // 如果不存在就将当前请求添加到pendingRequest
      cancelRequest.addPendingRequest(config);
        return config;
    }, err => {
        Promise.reject(err);
    });
    // 响应拦截器
    instance.interceptors.response.use(res => {
      // 移除成功请求记录
        cancelRequest.removeRequestKey(res.config)
        return res.data;
    }, err => {
      // 失败时也需要移除
        cancelRequest.removeRequestKey(err.config || {} )
        Promise.reject(err);
    });
    export default instance;
    

    这种方式虽然是可以取消重复请求,但是浏览的network中取消的请求会显示canceled状态,用户是有感知的!于是,就有了第二种方式,取消第二次请求,并实现用户无感。

    • 重复请求取消第二次
    1. 封装取消重复请求类
    import qs from 'qs'
    export default class CancelRequest {
      constructor() {
        this.pendingRequest = new Map()
      }
      // 根据请求信息生成唯一标识key
      geterateReqKey(config) {
        const { url, method, params, data } = config
        return [url, method, qs.stringify(params), qs.stringify(data)].join('&')
      }
      // 检查是否是重复请求,如果是取消第二次
      checkoutPendingRequest(config, CancelToken) {
        // 为每个请求添加cancelToken,同时拿到source获取到对每个请求取消请求的能力(cancel方法)
        let source = null
        if (config.cancelToken) {
          source = config.source
        } else {
          source = CancelToken.source()
          config.cancelToken = source.token
        }
        const requestKey = this.geterateReqKey(config)
        if (this.pendingRequest.has(requestKey)) {
          // 取消重复请求(第二次)
          source.cancel('double request:' + requestKey)
        } else {
          // 没重复就添加
          this.pendingRequest.set(requestKey, source)
        }
      }
      // 从请求列表中删除
      removeRequestKey(config) {
        // 延迟一点是为了避免用户快速多次点击提交,而第一次请求成功立刻清除掉,第二次请求不会被取消
        setTimeout(() => {
          const requestKey = this.geterateReqKey(config)
          this.pendingRequest.delete(requestKey)
        }, 200)
      }
    }
    
      1. 在axios拦截中使用
    // request.js
    import axios from 'axios';
    import CancelRequest from './cancel-request.js'
    // 实例化
    let cancelRequest = new CancelRequest()
    const instance = axios.create({
      // ...
    });
    
    // 请求拦截器
    instance.interceptors.request.use(config => {
    // 检查之前是否存在相同的请求,如果存在则取消。不存在就记录
      cancelRequest.checkoutPendingRequest(config);
        return config;
    }, err => {
        Promise.reject(err);
    });
    // 响应拦截器
    instance.interceptors.response.use(res => {
      // 移除成功请求记录
        cancelRequest.removeRequestKey(res.config)
        return res.data;
    }, err => {
        Promise.reject(err);
    });
    export default instance;
    

    至此,就分享完毕,希望大家有所获益!

    相关文章

      网友评论

          本文标题:axios取消重复请求实现

          本文链接:https://www.haomeiwen.com/subject/rtqxxrtx.html