axios 使用 cancel token 取消请求

axios 使用 cancel token 取消请求

业务场景

1. 在项目中切换路由,停止原路由中的正在请求的接口

在我参与的项目中有的页面在进入时会调用多个接口,并且这些接口可能会几秒后才请求完;在用户切换路由时这些接口就没有必要再请求了;

如果需求只是要在切换路由是取消之前的全部接口,使用下面的方法就可以实现;如果还有取消具体的某一个接口的需求,那么请看业务场景2的实现方式;

实现代码如下:

// 使用 CancelToken.source
// CancelToken.source 方法会构建一个 CancelToken 对象,并返回一个有两个参数的对象( token、cancel )
// token -> 构建出的 CancelToken 对象
// cancel -> 取消请求需要调用的方法
// 由于 CancelToken 对象是在拦截器外构建的,所有的接口中的 config.cancelToken 指向的都是同一个 CancelToken 对象,所以可以使用 source.cancel 方法取消所有的接口
import $store from '../store/index'; // 引入 store

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

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    
    
    config.cancelToken = source.token
    
    // 此处使用 store 存储取消接口的方法,便于在其他地方调用
    let arr = $store.getters.getCancelTokenList
    arr.push(source.cancel)
    $store.commit('setCancelTokenList', arr)
    
    // 我见过有人在 main.js 中直接声明变量( Vue.prototype.$sourceCancel = null ) ,拦截器中进行赋值,再在需要的地方调用
    // 不知道这种方法的好处或者坏处,如果有人了解,欢迎评论
    // $sourceCancel = source.cancel
    return config;
}, function (error) {
    
    
    // 预处理请求有异常(error)时抛出错误
    return Promise.reject(error);
});
// router.js
// 在路由守卫中设置切换路由后取消接口,并将存储方法的变量清空
router.beforeEach((to, from, next) => {
    
    
	console.log(this.$store.getters.getCancelTokenList[0])
	this.$store.getters.getCancelTokenList[0]('取消接口')
	$store.commit('setCancelTokenList', [])
	next()
})
2. 多次调用同一个接口时,需要取最新的接口返回信息

实现思路:取消接口的方法与场景1中的大致相同,但是由于需要对具体的某个接口进行取消,所以需要修改一下存储取消接口方法的变量,如下:

// 使用请求的 url 地址作为变量的 key 存储取消接口的方法
CancelTokenList = {
    
    
	url: function() {
    
    }
}
import $store from '../store/index'; // 引入 store
let CancelToken = axios.CancelToken;
let cancel = null

// 方法1 - 使用 CancelToken.source
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    
    
	// 将 CancelToken 对象的构建放在拦截器里面,就不会出现多个取消方法指向同一个对象
	let source = CancelToken.source();
    let obj = $store.getters.getCancelTokenList
    let url = config.url
    
    config.cancelToken = source.token
    if (config.method === 'get') {
    
    
        url = url.split('?')[0]
    }
    if (obj[url]) {
    
     // 在存储的对象中有当前 url 的变量,则调用方法取消请求
    	obj[url]('取消接口')
    	delete obj[url]
	}
	obj[url] = source.cancel
	$store.commit('setCancelTokenList', obj)
    return config;
}, function (error) {
    
    
    // 预处理请求有异常(error)时抛出错误
    return Promise.reject(error);
});

// 方法2 - 直接使用 CancelToken 的构造函数
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    
    
	let url = config.url
	let obj = $store.getters.getCancelTokenList
    if (config.method === 'get') {
    
    
        url = url.split('?')[0]
    }
    if (obj[url]) {
    
     // 在存储的对象中有当前 url 的变量,则调用方法取消请求
    	obj[url]('取消接口')
    	delete obj[url]
	}
    config.cancelToken = new CancelToken(function executor(c) {
    
    
        // executor 函数接收一个 cancel 函数作为参数
        cancel = c;
    })
    obj[url] = cancel
	$store.commit('setCancelTokenList', obj)
    return config;
}, function (error) {
    
    
    // 预处理请求有异常(error)时抛出错误
    return Promise.reject(error);
});
// router.js
// 使用 url 作为 key 的方式存储取消方法的话在路由守卫中就需要进行一次遍历
router.beforeEach((to, from, next) => {
    
    
	let obj = this.$store.getters.getCancelTokenList
	for (let key in obj) {
    
    
		obj[key]('取消接口')
	}
  $store.commit('setCancelTokenList', [])
  next()
})

问题

后台在写接口时可能会有如下两种写法:

let id = 123

axios.get('/test?id=' + id, {
    
    
  	cancelToken: source.token
}).catch(function(thrown) {
    
    
  	console.log('thrown', thrown);
});

axios.get('/test/' + id, {
    
    
  	cancelToken: source.token
}).catch(function(thrown) {
    
    
  	console.log('thrown', thrown);
});

在上面的实现方法中,我使用 url 作为 key 的形式进行遍历的存储时,如果是第二种形式的接口,会导致 url 无法作为唯一标识来使用,希望能有大佬有解决方法

猜你喜欢

转载自blog.csdn.net/weixin_42206013/article/details/120416765