JS使用axios取消请求 - Kaiqisan

JS使用axios取消请求

ヤッハロー、Kaiqisanすうう、一つふつうの学生プログラマである,这一次也是直面项目上遇到的问题。

这次内容有点多,如果您只是想复制代码的话,我做个目录,直接跳到目录找代码即可。


前端单方面违约取消请求,所以,这里的代码只有前端的事情。

官网写的取消法

/* a.js */
import axios from 'axios'

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


export default {
    
    
    getReq() {
    
    
        axios({
    
    
            methods: 'get',
            url: 'http://localhost:3000/testGet', 
            // 本地写了简易服务器,设置延时处理请求模拟网卡的情况,大家有能力的可以自己写一个服务器
            cancelToken: source.token	// 关键代码
        }).then(res => {
    
    
            console.log(res)
        }).catch(err => {
    
    
            console.log(err)
        })
    },

    cancelReq() {
    
    
        source.cancel('do cancel')
        source = CancelToken.source()	// 重新规划
    }
}

这些代码直接下载.vue .jsx文件中也可以,只要参数到位,找到合适的机会触发就可以了。

PS:上面的取消操作是AOE影响的,意思就是说它可以一次取消多次请求,如果一个人在短时间内发起多次请求,后端没来得及处理,让未处理的请求排起了队伍,一个取消操作就可以取消所有还未返回的请求。

///

现在我们来扒一下它的源码,康康这个过程是怎么样的呢!?

CancelToken = axios.CancelToken 

首先针对这句话来展开研究。

我们在输出栏打开源码,看到的是以下内容

function CancelToken(executor) {
    
    
  if (typeof executor !== 'function') {
    
    		// 非法情况处理
    throw new TypeError('executor must be a function.');
  }

  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    
    
    resolvePromise = resolve;		// 通过外部来影响这个异步函数的去向
  });

  var token = this;
  executor(function cancel(message) {
    
    
    if (token.reason) {
    
    		// 如果已经取消了,就不再执行第二次'取消请求'
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason); // 成功取消请求,返回参数reason
  });
}

/**
 * Throws a `Cancel` if cancellation has been requested.
 */
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
    
    
  if (this.reason) {
    
    	
    throw this.reason;	// 
  }
};

/**
 * Returns an object that contains a new `CancelToken` and a function that, when called,
 * cancels the `CancelToken`.
 */
CancelToken.source = function source() {
    
    
  var cancel;
  var token = new CancelToken(function executor(c) {
    
    
    cancel = c;
  });
  return {
    
    
    token: token,
    cancel: cancel
  };
};

module.exports = CancelToken;

然后是第二句话,source = CancelToken.source(),它对于上面源码的最后一个函数,通过打印,我们可以看到这个CancelToken.source()返回了一个对象,它有俩成员,一个是token异步函数(原型是Promise),一个是cancel方法。

然后基本的命名都OK了,接下来就可以正式投入使用了。

在请求中触发了cancel()方法之后,这个方法被执行(自己去上面源码找)

function cancel(message) {
    
    
    if (token.reason) {
    
    		// 如果已经取消了,就不再执行第二次'取消请求'
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason); // 成功取消请求,返回参数reason
}

然后触发resolvePromise,走then路线

if (config.cancelToken) {
    
     // 检测原生的请求里是否有cancelToken,如果没有就不执行下面的方法
	config.cancelToken.promise.then(function onCanceled(cancel) {
    
    
		if (!request) {
    
    		// 没有请求直接劝退
			return;
		}
		request.abort();	// 原生的取消请求方法
		reject(cancel);		// 请求那边走catch路线,返回cancel参数
    	// 复位数据
		request = null;		// 清空请求
	});
}

至于在取消请求之后再次调用CancelToken.source() 复位的问题,如果不复位的话,下次再发请求的话就会直接取消,因为此时的CancelToken凭证已经被触发,想要验证的话可以在请求取消前后查看CancelToken.source(),我们会发现在请求取消后,它的token参数中多了一个reason属性。这就表示请求已经被取消。凭证是一次性的,如果发现凭证有reason属性,就表示需要取消请求。所以需要复位。

取消方法2

上面已经讲了原理,下面就不会再详细展开

/* a.js */
import axios from 'axios'

let CancelToken = axios.CancelToken
let cancel		// 取消参数


export default {
    
    
    getReq() {
    
    
        axios({
    
    
            methods: 'get',
            url: 'http://localhost:3000/testGet', 
            // 本地写了简易服务器,设置延时处理请求模拟网卡的情况,大家有能力的可以自己写一个服务器
            cancelToken: new CancelToken(function executor(c) {
    
     
    			cancel = c;
  			}) // 等同于上面的 source.token
        }).then(res => {
    
    
            console.log(res)
        }).catch(err => {
    
    
            console.log(err)
        })
    },

    cancelReq() {
    
    
        cancel('do cancel')
    }
}

PS:executor函数内的参数c为函数

function cancel(message) {
    
    
    if (token.reason) {
    
    		// 如果已经取消了,就不再执行第二次'取消请求'
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason); // 成功取消请求,返回参数reason
}

它只能拦截当前请求,无法拦截所有已经发出去的请求,所以在开发的时候请做好防滑操作!

更加高度的封装,使用axios.create之后的取消方法

/* 配置文件 */
import axios from 'axios'

// 创建axios实例
const service = axios.create({
    
    
    baseURL: configJS.BASE_API,
})


// request拦截器
service.interceptors.request.use(
    ...........// 请求拦截
)

// respone拦截器
service.interceptors.response.use(
    ............
)

export default service

配置文件就正常创建

/* api仓库 */
import axios from 'axios'
import request from '@/utils/request'	// axios配置文件

getInfo(data, that) {
    
    
    return request({
    
    
        url: 'xxxxxxxx', // 自己定
        method: 'get',	// 只适合get请求的取消
        params: data,	// 参数
        cancelToken: new axios.CancelToken(function executor(c) {
    
    
            that.source = c
        })
    })
},
/* 开始正式调用 */

exeGetInfo() {
    
    
    api.getInfo({
    
     id: 10 }, this).then(res => {
    
    
		.........
	}).catch(err => {
    
    
	   	........
	})
}

cancelQuest() {
    
    
    if (typeof this.source === 'function') {
    
    
        this.source('终止请求') //取消请求
    }
},

后来,我就在思索这个中断请求的原理,本来以为是前端再次向后端发送一个拒收的请求,但是,我打开服务器之后,发现在取消请求的时候并没有再一次收到第二条请求,所以,我就怀疑是前端自己直接拒收后端发来的请求。

下面是我的后端node.js代码

/* index.js主入口文件 */

const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const cors = require('cors');


let router = require('./routes/routers')


app.all('*', function(req, res, next) {
    
    
	console.log(req.method);
	res.header("Access-Control-Allow-Origin", "*");
	res.header('Access-Control-Allow-Headers', 'Content-type');
	res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,PATCH");
	res.header('Access-Control-Max-Age', 1728000); //预请求缓存20天
	next();
});
app.use(cors({
    
    
	origin: ['http://localhost:8800'],
	methods: ['GET', 'POST'],
	allowHeader: ['Content-Type', 'Authorization']
}))
app.use(bodyParser.urlencoded({
    
    
	extended: false
}))
app.use(bodyParser.json())

app.use(router)

app.listen(3000, () => {
    
    
	console.log('服务器启动')
})
/* routers.js路由文件 */

const express = require('express')
const router = express.Router()

router.get('/testGet', (req, res) => {
    
    
    setTimeout(() => {
    
     // 增加处理时间,便于测试setTimeout
        console.log('gggggggggggggg');
        res.json([		// 返回的信息
            {
    
    
                name: 'Kaiqisan',uid: 100
            },
            {
    
    
                name: 'Tom',uid: 101
            }
        ])
    }, 3000)
})

module.exports = router

后来发现,即使我取消了请求,后端还是会向前端发送信息,那就是说我前端拒收之后后端发来什么,我都不会去理会,直接丢弃。在axios方法这里,直接走catch路线,报错完事,方法结束了。所以,以此类推,这个方法只能取消get请求,而不会取消post等其他需要后端对后台数据做出修改操作的请求,除非在触发取消操作的时候再次要求后端取消相关的操作。

总结

取消请求是很重要的方法,虽然不会减少后端的负担(服务器:我谢谢你!),但是,可以显著减少前端的内存消耗,更少的请求代表更少的捕获数据次数,也代表更少的无用数据渲染和赋值,也会适量减轻前端项目工程的复杂度,再配合防滑锁,完美!

猜你喜欢

转载自blog.csdn.net/qq_33933205/article/details/108127541