【Note】axios取消请求

74 阅读2分钟

一、大致思路

一旦生成请求,将请求以key, value的形式存入一个Map数据类型的容中,然后在请求拦截器中先对当前请求在Map中进行查重处理,重复则调用AbortControl.abort()取消;如果没重复,则加入到Map中。

二、开整

1.封装一个Map容器类 PendingRequestMap

// cancelPendingRequest.js
import axios from 'axios'
import qs from 'qs'

class PendingRequestMap {
    constructor() {
        this.map = new Map()
        // this.controller = new AbortController()
    }
    
    // 生成唯一key
    getRequestKey(config) {
        const { url, method, params, data } = config
        const key = [url, method, qs.stringify(params), qs.stringify(data)].join('#')
        return key
    }
    // 将新的请求放入map中
    addPendingRequest(config) {
        const key = this.getRequestKey(config)
        config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
		if (!this.map.has(key)) {
                    this.map.set(key, cancel)
		}
	})
    }
    
    // 移除map中的请求
    removePendingRequest(config){
        const key = this.getRequestKey(config)
        if (this.map.has(key)) {
            const cancel = this.map.get(key)
            cancel('canceled')
            this.map.delete(key)
	}
    }
    
    // 取消所有pengding请求
    clearAllPendingRequest() {
        // 遍历,挨个取消请求
        for (const cancel of this.map.values()) {
            cancel('canceled')
	}
        // 清空容器
	this.map.clear()
    }
}

export default new PendingRequestMap()

AbortController版本

// cancelPendingRequest.js
import qs from 'qs'

class PendingRequestMap {
  constructor() {
    this.reqMap = new Map()
  }

  // 生成唯一key
  getRequestKey(config) {
    const { url, method, params, data } = config
    const key = [url, method, qs.stringify(params), qs.stringify(data)].join('#')
    return key
  }
  // 将新的请求放入map中
  addPendingRequest(config) {
    const key = this.getRequestKey(config)
    if (!this.reqMap.has(key)) {
      const controller = new AbortController()
      config.signal = controller.signal
      this.reqMap.set(key, controller)
    }
  }

  // 移除map中的请求并取消
  removePendingRequest(config) {
    console.log(config, 'config');
    const key = this.getRequestKey(config)
    if (this.reqMap.has(key)) {
      let controller = this.reqMap.get(key)
      controller.abort()
      controller = null
      this.reqMap.delete(key)
    }
  }

  // 取消所有pending请求
  clearAllPendingRequest() {
    // 遍历,挨个取消请求
    for (let controller of this.reqMap.values()) {
      controller.abort()
      controller = null
    }
    // 清空容器
    this.reqMap.clear()
  }
}

export default new PendingRequestMap()

2.在request.js中调用

请求前对重复请求操作和将新的请求放入容器中,请求结束后移除

// request.js
import pendingRequestMap from '@/utils/cancelPendingRequest.js'

/**
 * 请求拦截器
 */
axios.interceptors.request.use(config => {
	// 在请求开始前,对之前的请求做检查取消操作
	pendingRequestMap.removePendingRequest(config)
	// 将当前请求添加到 pending 中
	pendingRequestMap.addPendingRequest(config)

	// do something.....
	return config
}, error => {
	return Promise.reject(error.response)
})

/**
 * 响应拦截器
 */
service.interceptors.response.use(response => {
    // 在请求结束后,移除本次请求
    pendingRequestMap.removePendingRequest(response.config)

    // do something...
    return Promise.resolve(response.data)
}, error => {
    return Promise.reject(error.response.data)
})

3.跳转页面后,取消上个页面未完成的请求

在路由全局导航守卫中这么写

import pendingRequestMap from '@/utils/cancelPendingRequest.js'

router.beforeEach(async (to, from, next) => {
    //清空pending请求
    pendingRequestMap.clearAllPendingRequest()
    
    next()
}

结语

代码中如有需要更正和修改的地方,欢迎评论讨论。