axios取消重复请求不会阻止请求到服务器

·  阅读 3059

axios取消重复请求CancelToken可以让同一个请求遭到用户多次触发时,只返回最近的一次请求结果,而取消之前的请求

在业务生产过程中,这个优化可以增强用户体验,减少不必要的数据接收和页面渲染。

使用它很简单,我们只要对axios进行一层封装:

// 封装axios请求
import axios from 'axios'
import config from '@/config'
import errorHandle from './errorHandle'
const CancelToken = axios.CancelToken

class HttpRequest {
  constructor(baseURL) {
    this.baseURL = baseURL
    this.pending = {}
  }
  // 获取预设的配置
  getInsideConfig() {
    return {
      baseURL: this.baseURL,
      headers: { 'Content-Type': 'application/json;charset=utf-8' },
      timeout: 10000,
      withCredentials: false // 跨域时是否使用凭证,默认false
    }
  }
  // 取请求url、method、params、data等组成key,标识这次请求
  getPendingKey(config) {
    return [
      config.url,
      config.method,
      JSON.stringify(config.params),
      JSON.stringify(config.data)
    ].join('&')
  }
  // 移除请求pending
  removePending(key, isRequest = false) {
    if (this.pending[key] && isRequest) {
      this.pending[key]('取消重复请求')
    }
    delete this.pending[key]
  }
  // 拦截处理
  interceptors(instance) {
    // 添加请求拦截器
    instance.interceptors.request.use(
      config => {
        // 1、取请求标识key
        // 2、移除上一次该请求标识(如果有,则调用cancelToken取消上次请求)
        // 3、给这次请求增加cancelToken备用(如果后续有重复请求则调用)
        const key = this.getPendingKey(config)
        this.removePending(key, true)
        config.cancelToken = new CancelToken(cancel => {
          this.pending[key] = cancel
        })
        return config
      },
      error => {
        // 对请求错误做些什么
        errorHandle(error)
        return Promise.reject(error)
      }
    )

    // 添加响应拦截器
    instance.interceptors.response.use(
      response => {
        // 请求返回结果,移除pending
        const key = this.getPendingKey(response.config)
        this.removePending(key)
        if (response.status === 200) {
          return Promise.resolve(response.data)
        } else {
          return Promise.reject(response)
        }
      },
      error => {
        // 对响应错误做些什么
        errorHandle(error)
        return Promise.reject(error)
      }
    )
  }
  // 发送请求
  request(config) {
    const instance = axios.create()
    const newOptions = Object.assign(this.getInsideConfig(), config)
    this.interceptors(instance)
    return instance(newOptions)
  }

  // 语法糖,get请求
  get(url, config) {
    const options = Object.assign(
      {
        url,
        method: 'get'
      },
      config
    )
    return this.request(options)
  }
  // 语法糖,post请求
  post(url, data, config) {
    const options = Object.assign(
      {
        url,
        data,
        method: 'post'
      },
      config
    )
    return this.request(options)
  }
  // 语法糖,put请求
  put(url, data, config) {
    const options = Object.assign(
      {
        url,
        data,
        method: 'put'
      },
      config
    )
    return this.request(options)
  }
  // 语法糖,delete请求
  delete(url, config) {
    const options = Object.assign(
      {
        url,
        method: 'delete'
      },
      config
    )
    return this.request(options)
  }
}

HttpRequest.prototype.all = axios.all
HttpRequest.prototype.spread = axios.spread

export default new HttpRequest(config.baseURL)

复制代码

这样在我们短时间内多次触发同一个请求时,在网络较慢,前一个请求还在pendding状态下,axios就可以取消掉前一次的请求

image-20200618110121130

但是该做法会潜藏一个问题,就是这些被取消的请求,还是会发给后端。这个问题导致的后果就是,比如我点击按钮插入数据,连续点击5次,虽然重复的被取消了,但是在后端依然接受到这5次请求,并执行了5次插入数据。

我在后端接口中埋了打印点,当被请求时触发:

image-20200618110231931

所以以前刚开始理解的axios通过CancelToken取消重复请求,会把之前的取消掉,请求不会发出去给后端,这是错误的理解

只要点击了按钮触发事件,请求就发出去了,尽管可以取消重复请求,只要网络还在连接,后端还是会一一收到所有的请求,该查库的查库,该创建的创建,只是重复请求返回的数据被前端取消了而已,前端只接受最后一次数据,渲染一次页面。

针对该问题,可以有多种优化方式,比如后端可以做控制,对于重复创建等操作进行限制,而在前端可以在按钮点击后显示loading状态等,以限制用户重复点击触发。

分类:
前端
标签:
分类:
前端
标签: