核心代码
import axios from 'axios'
// Base64可自行安装依赖或实现
import Base64 from './Base64'
class PendingPromiseStorage {
constructor() {
this._data = new Map()
}
setItem(payload) {
let { uid, item } = payload
this._data.set(uid, item)
}
getItem(uid) {
return this._data.get(uid)
}
removeItem(uid) {
this._data.delete(uid)
}
hasItem(uid) {
return this._data.has(uid)
}
cancelBefore(config) {
let { uid, url } = config
if (!this.hasItem(uid)) {
return
}
let item = this.getItem(uid)
let msg = `axios cancel (before cancel) ${url}`
item.cancel(msg)
this.removeItem(uid)
}
cancelAfter(config, cancel) {
let { uid, url } = config
if (!this.hasItem(uid)) {
return
}
let item = this.getItem(uid)
let msg = `axios cancel (after cancel) ${url}`
if (cancel !== item.cancel) {
setTimeout(() => {// 想看浏览器Network有记录,启用延迟
cancel(msg)
})
}
}
}
PendingPromiseStorage.getPendingUID = (config) => {
return Base64.encode(`${config.url}${config.method}${JSON.stringify(config.params)}${typeof config.data === 'string' ? config.data : JSON.stringify(config.data)}`)
}
const pendingPromiseStorage = new PendingPromiseStorage()
const interceptors = {
request: {
success(config) {
console.log('cancelToken:', config.cancelToken)
if (Object.hasOwnProperty.call(config, 'cancelToken')
&& (config.cancelToken === 'before' || config.cancelToken === 'after')
) {
let cancelToken = config.cancelToken
let uid = config.uid = PendingPromiseStorage.getPendingUID(config)
if (cancelToken === 'before') {
pendingPromiseStorage.cancelBefore(config)
}
let cancel = null
config.cancelToken = new axios.CancelToken(c => {
cancel = c
let item = {
uid,
createTime: Date.now(),
cancelToken,
cancel: c
}
if (
(cancelToken === 'before')
|| (cancelToken === 'after' && !pendingPromiseStorage.hasItem(uid))
) {
pendingPromiseStorage.setItem({ uid, item })
}
})
if (cancelToken === 'after') {
pendingPromiseStorage.cancelAfter(config, cancel)
}
}
return config
},
failed(error) {
let { config = {} } = error
pendingPromiseStorage.removeItem(config.uid)
return Promise.reject(error)
}
},
response: {
success(response) {
let { config } = response
pendingPromiseStorage.removeItem(config.uid)
return response
},
failed(error) {
let { config = {} } = error
pendingPromiseStorage.removeItem(config.uid)
return Promise.reject(error)
}
}
}
class Request extends axios.create {
constructor(config) {
super(config)
this.interceptors.request.use(interceptors.request.success, interceptors.request.failed)
this.interceptors.response.use(interceptors.response.success, interceptors.response.failed)
}
}
const instanceInterceptors = {
request: {
success(config) {
// TODO
return config
},
failed(error) {
return Promise.reject(error)
}
},
response: {
success(config) {
// TODO
return config
},
failed(error) {
return Promise.reject(error)
}
}
}
let instance = new Request({
name: 'Test-cancelToken',
baseURL: '/dev-api/',
timeout: 10000
})
instance.interceptors.request.use(instanceInterceptors.request.success, instanceInterceptors.request.failed)
instance.interceptors.response.use(instanceInterceptors.response.success, instanceInterceptors.response.failed)
export default instance
使用代码
export default {
methods: {
login() {
return request({
url: '/user/login',
method: 'post',
data: {
username: 'admin',
},
cancelToken: 'before'
// cancelToken: 'after'
})
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
}
}
}
// axios/lib/cancel/CancelToken.js
'use strict';
var Cancel = require('./Cancel');
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
/**
* 定义一个将来能执行取消请求的promise对象,当这个promise的状态为完成时(fullfilled),
* 就会触发取消请求的操作(执行then函数)。而执行resolve就能将promise的状态置为完成状态。
* 这里把resolve赋值给resolvePromise,就是为了在这个promise外能执行resolve而改变这个promise的状态
* 注意这个promise对象被赋值给CancelToken实例的属性promise,将来定义then函数就是通过这个属性得到promise
*/
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
/**
* 将CancelToken实例赋值给token
* 执行executor函数,将cancel方法传入executor,
* cancel方法可调用resolvePromise方法,即触发取消请求的操作
*/
var token = this;
executor(function cancel(message) {
if (token.reason) {
// 取消已响应 返回
return;
}
token.reason = new Cancel(message);
// 这里执行的就是promise的resolve方法,改变状态
resolvePromise(token.reason);
});
}
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
// 这里可以看清楚source函数的真面目
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
// c 就是CancelToken中给executor传入的cancel方法
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
module.exports = CancelToken;
// axios/lib/adapters/xhr.js
// 创建XHR对象
var request = new XMLHttpRequest()
// 模拟当前ajax请求
request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true)
// 定义取消请求promise对象的then函数
if (config.cancelToken) { // 如果配置了cancelToken属性
// 当promise为完成态时,这个then函数执行,即执行取消请求
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
// 取消ajax请求
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
当使用new axios.cancelToken((c) => xx = c) 这里就是把一个执行函数放进去,然后在cancelToken中会new一个Promise 把promise的resolve丢到外面,然后再执行前面传进来的函数,我们传进来的函数中写一个参数就是c 这个c就是用来取消的,当c里面就是把这个resolve执行 执行了就会触发 promise的then 在then中使用xmlhttprequest.abort()的方法中断这个请求。
浙公网安备 33010602011771号