停止javascript的ajax请求,取消axios请求,取消reactfetch请求
qiyuwang 2024-10-21 09:39 20 浏览 0 评论
一、Ajax
原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。
停止javascript的ajax请求有两种方式
1. 设置超时时间让ajax自动断开
2. 手动去停止ajax请求,
核心是调用XMLHttpRequest对象上的abort方法
我们以jquery举例说明:
jquery的ajax对象的abort方法:调用abort后jquery会执行error的方法,抛出abort的异常信息,此时即可执行我们中断ajax后的操作。
var ajax = $.ajax({
'error':function(jqXHR, textStatus, errorThrown){
if(errorThrown != 'abort'){
//ajax被调用abort后执行的方法
alert('您的ajax方法被停止了');
}
}
})
ajax.abort();//停止ajax
原生JS
xmlHttp.open("POST","Url",true);
xmlHttp.onreadystatechange=function(){
...//得到响应之后的操作
}
xmlHttp.send();
//设置3秒钟后检查xmlHttp对象所发送的数据是否得到响应.
setTimeout("CheckRequest()","3000");
function CheckRequest(){
//为4时代表请求完成了
if(xmlHttp.readyState!=4){
alert('数据响应超时');
//关闭请求
xmlHttp.close();
}
}
完整板:
<div class="page" id="app">
<button class="get-msg">获取数据</button>
<button class="cancel">取消获取</button>
</div>
<script>
var currentAjax = null
$('.get-msg').click(function () {
currentAjax = $.ajax({
type: 'GET',
url: 'http://jsonplaceholder.typicode.com/comments',
success: function (res) {
console.log(res)
},
error: function (err) {
console.log("获取失败")
}
})
})
$('.cancel').click(function () {
if (currentAjax) {
currentAjax.abort()
}
})
</script>
切记:不可用abort方法来作为终止对服务器的请求操作,只有当做在前端页面立刻停止执行ajax成功后的方法,因为你执行abort方法后,ajax很可能已经对服务端发送了请求,只是还未返回回馈信息而已。
二、Axios:
关键词:CancelToken、source、source.token
应用场景:tab页频繁切换,页面的切换,用于终止正在挂起的请求
axios 的 cancelToken
axios是一个主流的http请求库,它提供了两种取消请求的方式。
项目的如何应用
- 配置参数
axios({
method: 'post',
url: url,
withCredentials: false,
params: params,
data: data,
cancelToken:data.cancelToken,
headers: headerParam
}).then(resp => {
resolve(resp)
}).catch(error => {
reject(error)
})
在调用请求那传入令牌cancelToken:data.cancelTokendata为传入的参数
调用接口前,传入上一步需要的令牌
let CancelToken = this.axios.CancelToken;this.source = CancelToken.source();data.cancelToken = this.source.token;
axios 在 main.js中挂载到了 实例中, data data 为请求的参数,this.source中有token令牌和取消请求的cancel方法
终止请求
cancelRequest(){
this.source.cancel("异常信息,选填")
},
在发起新的请求的时候,执行一下this.source.cancel()即可终止正在挂起的请求。
完整版:
<body>
<div class="page" id="app">
<button @click="getMsg" class="get-msg">获取数据</button>
<button @click="cancelGetMsg" class="cancel">取消获取</button>
<ul>
<li v-for="item in items">{{item.name}}</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
items: [],
cancel: null
},
methods: {
getMsg () {
let CancelToken = axios.CancelToken
let self = this
axios.get('http://jsonplaceholder.typicode.com/comments', {
cancelToken: new CancelToken(function executor(c) {
self.cancel = c
console.log(c)
// 这个参数 c 就是CancelToken构造函数里面自带的取消请求的函数,这里把该函数当参数用
})
}).then(res => {
this.items = res.data
}).catch(err => {
console.log(err)
})
//手速够快就不用写这个定时器了,点击取消获取就可以看到效果了
setTimeout(function () {
//只要我们去调用了这个cancel()方法,没有完成请求的接口便会停止请求
self.cancel()
}, 100)
},
//cancelGetMsg 方法跟上面的setTimeout函数是一样的效果,因为手速不够快,哦不,是因为网速太快,导致我来不及点取消获取按钮,数据就获取成功了
cancelGetMsg () {
// 在这里去判断你的id 1 2 3,你默认是展示的tab1,点击的时候不管你上一个请求有没有执行完都去调用这个cancel(),
this.cancel()
}
}
})
</script>
</body>
axios 的 cancelToken
axios是一个主流的http请求库,它提供了两种取消请求的方式。
- 通过axios.CancelToken.source生成取消令牌token和取消方法cancel
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
通过axios.CancelToken构造函数生成取消函数
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
});
// cancel the request
cancel();
需要注意的是在catch中捕获异常时,应该使用axios.isCancel()判断当前请求是否是主动取消的,以此来区分普通的异常逻辑。
封装取消请求逻辑
上面有两种取消请求,用哪种都是可以的,这里使用第二种。
取消请求主要有两个场景:
- 当请求方式method,请求路径url,请求参数(get为params,post为data)都相同时,可以视为同一个请求发送了多次,需要取消之前的请求
- 当路由切换时,需要取消上个路由中未完成的请求
我们封装几个方法:
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
const pending = new Map()
/**
* 添加请求
* @param {Object} config
*/
const addPending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data)
].join('&')
config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
if (!pending.has(url)) { // 如果 pending 中不存在当前请求,则添加进去
pending.set(url, cancel)
}
})
}
/**
* 移除请求
* @param {Object} config
*/
const removePending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data)
].join('&')
if (pending.has(url)) { // 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
const cancel = pending.get(url)
cancel(url)
pending.delete(url)
}
}
/**
* 清空 pending 中的请求(在路由跳转时调用)
*/
export const clearPending = () => {
for (const [url, cancel] of pending) {
cancel(url)
}
pending.clear()
}
Map是ES6中一种新型的数据结构,本身提供了诸多方法,方便操作,适合当前场景。如果不熟悉的可以查看ECMAScript 6 入门。
在给config.cancelToken赋值的时候,需要判断当前请求是否已经在业务代码中使用了cancelToken
qs是一个专门用来转换对象和字符串参数的库,最初是由 TJ 创建并维护的,也是axios推荐使用的参数序列化库。这里我们的目的只是单纯的将参数对象转换为字符串方便拼接。
Map结构默认部署了Symbol.iterator属性,可以使用for...of循环直接获取键名和键值,当然你也可以使用for...in循环。
在 axios 拦截器中使用
主要的方法已经写好了,只需要添加到axios拦截器中就可以了。
axios.interceptors.request.use(config => {
removePending(options) // 在请求开始前,对之前的请求做检查取消操作
addPending(options) // 将当前请求添加到 pending 中
// other code before request
return config
}, error => {
return Promise.reject(error)
})
axios.interceptors.response.use(response => {
removePending(response) // 在请求结束后,移除本次请求
return response
}, error => {
if (axios.isCancel(error)) {
console.log('repeated request: ' + error.message)
} else {
// handle error code
}
return Promise.reject(error)
})
将clearPending()方法添加到vue路由钩子函数中
router.beforeEach((to, from, next) => {
clearPending()
// ...
next()
})
三、fetch
以下是取消 Fetch 请求的基本步骤:
const controller = new AbortController();
const { signal } = controller;
fetch("http://localhost:8000", { signal }).then(response => {
console.log(`Request 1 is complete!`);
}).catch(e => {
console.warn(`Fetch 1 error: ${e.message}`);
});
// Abort request
controller.abort();
在 abort 调用时发生 AbortError,因此你可以通过比较错误名称来侦听 catch 中的中止操作。
}).catch(e => {
if(e.name === "AbortError") {
// We know it's been canceled!
}
});
将相同的信号传递给多个 fetch 调用将会取消该信号的所有请求:
const controller = new AbortController();
const { signal } = controller;
fetch("http://localhost:8000", { signal }).then(response => {
console.log(`Request 1 is complete!`);
}).catch(e => {
console.warn(`Fetch 1 error: ${e.message}`);
});
fetch("http://localhost:8000", { signal }).then(response => {
console.log(`Request 2 is complete!`);
}).catch(e => {
console.warn(`Fetch 2 error: ${e.message}`);
});
// Wait 2 seconds to abort both requests
setTimeout(() => controller.abort(), 2000);
样板:
function abortableFetch(request, opts) {
const controller = new AbortController();
const signal = controller.signal;
return {
abort: () => controller.abort(),
ready: fetch(request, { ...opts, signal })
};
}
说实话,我对取消 Fetch 的方法并不感到兴奋。在理想的世界中,通过 Fetch 返回的 Promise 中的 .cancel() 会很酷,但是也会带来一些问题。无论如何,我为能够取消 Fetch 调用而感到高兴,你也应该如此!
使用AbortController来取消 fetch
AbortController 是 JavaScript 的最新版本中的特性,它是在 fetch 被实现之后出现的。 更好的消息是所有现代浏览器都支持它。
AbortController 包含一个 abort 方法。 它还包含一个可以传递给fetch的signal属性。 当调用 AbortController.abort 时,fetch请求就会被取消。
让我们在 getCharacter 的fetch请求中使用 AbortController 及其signal属性:
function getCharacter(id: number) {
// 获取AbortController实例
const controller = new AbortController();
// 获取 signal属性
const signal = controller.signal;
const promise = new Promise(async (resolve) => {
const response = await fetch(`https://swapi.dev/api/people/${id}/`, {
method: "get",
// 将 signal作为fetch的参数之一
signal,
});
const data = await response.json();
assertIsCharacter(data);
resolve(data);
});
// 设置一个 取消函数
promise.cancel = () => controller.abort();
return promise;
}
我们从getCharacter函数中删除了async关键字,并将现有代码包装在一个新的Promise中。 当请求到数据后,我们使用resolve将数据抛出去。 我们在Promise中添加了一个cancel方法,该方法调用了AbortController.abort。
包含 cancel 方法的 Promise 从 getCharacter 中被返回,以便我们在业务代码中可以使用它来取消请求。
保存代码查看界面效果,发现有一个类型错误:
// - Property 'cancel' does not exist on type 'Promise<unknown>'
promise.cancel = () => controller.abort();
让我们为Promise的 cancel 方法创建一个类型
interface PromiseWithCancel<T> extends Promise<T> {
cancel: () => void;
}
// 使用 然使用类型断言来解决前面的类型错误
function getCharacter(id: number) {
...
(promise as PromiseWithCancel<Character>).cancel = () => controller.abort();
return promise as PromiseWithCancel<Character>;
}
在 React 组件中使用getCharacter
我们将把getCharacter返回的 promise 存储在一个名为query的状态变量中。
export function App() {
const [status, setStatus] = React.useState<"loading" | "loaded" | "cancelled">("loading");
const [data, setData] = React.useState<Character | undefined>(undefined);
const [query, setQuery] = React.useState<PromiseWithCancel<Character> | undefined>(undefined);
React.useEffect(() => {
const q = getCharacter(1);
setQuery(q);
q.then((character) => {
setData(character);
setStatus("loaded");
});
}, []);
...
现在,当点击取消按钮的时候,我们调用 promise 中的cancle方法
<button
onClick={() => {
query?.cancel();
setStatus("cancelled");
}}
>
Cancel
</button>
点击取消按钮后吗,我们看到发现’Cancelled‘文案被渲染出来了
编辑
添加图片注释,不超过 140 字(可选)
再通过谷歌开发者工具查看网络请求,发现请求也被取消了
?
编辑切换为居中
添加图片注释,不超过 140 字(可选)
捕获"取消请求"发生的错误
让我们再看看控制台,当请求被取消后,有错误被抛出了
?
编辑切换为居中
添加图片注释,不超过 140 字(可选)
我们可以使用 try catch 来包裹请求以便捕获错误
const promise = new Promise(async (resolve) => {
try {
const response = await fetch(`https://swapi.dev/api/people/${id}/`, {
method: "get",
signal,
});
const data = await response.json();
assertIsCharacter(data);
resolve(data);
} catch (ex: unknown) {
if (isAbortError(ex)) {
console.log(ex.message);
}
}
});
isAbortError类型的函数如下
function isAbortError(error: any): error is DOMException {
if (error && error.name === "AbortError") {
return true;
}
return false;
}
现在当我们再次点击取消按钮,我们会在控制台收到一条消息提示而不是一个错误
?
编辑
添加图片注释,不超过 140 字(可选)
总结
可以将AbortController中的signal属性传递给fetch。 然后可以调用AbortController.abort取消请求。
取消 fetch 会引发一个错误,但可以使用try catch将其捕获。
相关推荐
- 基于Docker方式安装与部署Camunda流程引擎
-
1Camunda简介官网:https://docs.camunda.org/manual/7.19/installation/docker/Camunda是一个轻量级、开源且高度灵活的工作流和决策自...
- 宝塔Linux面板如何部署Java项目?(宝塔面板 linux)
-
通过宝塔面板部署Java还是很方便的,至少不需要自己输入tomcat之类的安装命令了。在部署java项目前,我还是先说下目前的系统环境,如果和我的系统环境不一样,导致部署不成功,那你可能需要去找其他资...
- 浪潮服务器如何用IPMI安装Linux系统
-
【注意事项】此处以浪潮服务器为例进行演示所需使用的软件:Chrome浏览器个人PC中需要预先安装java,推荐使用jdk-8u181-windows-x64.exe【操作步骤】1、在服务器的BIOS中...
- Centos7环境Hadoop3集群搭建(hadoop集群环境搭建实验报告)
-
由于项目需要存储历史业务数据,经过评估数据量会达到100亿以上,在原有mongodb集群和ES集群基础上,需要搭建Hbase集群进行调研,所以首先总结一下Hadoop集群的搭建过程。一、三个节点的集群...
- Hadoop高可用集群搭建及API调用(hadoop高可用原理)
-
NameNodeHA背景在Hadoop1中NameNode存在一个单点故障问题,如果NameNode所在的机器发生故障,整个集群就将不可用(Hadoop1中虽然有个SecorndaryNameNo...
- 使用Wordpress搭建一个属于自己的网站
-
现在开源的博客很多,但是考虑到wordpress对网站的seo做的很好,插件也多。并且全世界流量排名前1000万的网站有33.4%是用Wordpress搭建的!所以尝试用Wordpress搭建一个网站...
- Centos 安装 Jenkins(centos 安装ssh)
-
1、Java安装查看系统是否已安装Javayumlistinstalled|grepjava...
- Java教程:gitlab-使用入门(java中的git)
-
1导读本教程主要讲解了GitLab在项目的环境搭建和基本的使用,可以帮助大家在企业中能够自主搭建GitLab服务,并且可以GitLab中的组、权限、项目自主操作...
- Dockerfile部署Java项目(docker部署java应用)
-
1、概述本文主要会简单介绍什么是Docker,什么是Dockerfile,如何安装Docker,Dockerfile如何编写,如何通过Dockerfile安装jar包并外置yaml文件以及如何通过do...
- 如何在Eclipse中搭建Zabbix源码的调试和开发环境
-
Zabbix是一款非常优秀的企业级软件,被设计用于对数万台服务器、虚拟机和网络设备的数百万个监控项进行实时监控。Zabbix是开放源码和免费的,这就意味着当出现bug时,我们可以很方便地通过调试源码来...
- Java路径-02-Java环境配置(java环境搭建及配置教程)
-
1Window环境配置1.1下载...
- 35.Centos中安装python和web.py框架
-
文章目录前言1.Centos7python:2.Centos8python:3.进行下载web.py框架然后应用:4.安装好之后进行验证:5.总结:前言...
- 《我的世界》服务器搭建(我的世界服务器如何搭建)
-
1.CentOS7环境1.1更改YUM源#下载YUM源文件curl-o/etc/yum.repos.d/CentOS-Base.repohttps://mirrors.aliyun.com...
- CentOS 7 升级 GCC 版本(centos7.4升级7.5)
-
1.GCC工具介绍GCC编译器:...
- Linux安装Nginx详细教程(linux安装配置nginx)
-
环境准备1.因为Nginx依赖于gcc的编译环境,所以,需要安装编译环境来使Nginx能够编译起来。命令:yuminstallgcc-c++显示完毕,表示安装完成:2.Nginx的http模块需要...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 基于Docker方式安装与部署Camunda流程引擎
- 宝塔Linux面板如何部署Java项目?(宝塔面板 linux)
- 浪潮服务器如何用IPMI安装Linux系统
- Centos7环境Hadoop3集群搭建(hadoop集群环境搭建实验报告)
- Hadoop高可用集群搭建及API调用(hadoop高可用原理)
- 使用Wordpress搭建一个属于自己的网站
- Centos 安装 Jenkins(centos 安装ssh)
- Java教程:gitlab-使用入门(java中的git)
- Dockerfile部署Java项目(docker部署java应用)
- 如何在Eclipse中搭建Zabbix源码的调试和开发环境
- 标签列表
-
- navicat无法连接mysql服务器 (65)
- 下横线怎么打 (71)
- flash插件怎么安装 (60)
- lol体验服怎么进 (66)
- ae插件怎么安装 (62)
- yum卸载 (75)
- .key文件 (63)
- cad一打开就致命错误是怎么回事 (61)
- rpm文件怎么安装 (66)
- linux取消挂载 (81)
- ie代理配置错误 (61)
- ajax error (67)
- centos7 重启网络 (67)
- centos6下载 (58)
- mysql 外网访问权限 (69)
- centos查看内核版本 (61)
- ps错误16 (66)
- nodejs读取json文件 (64)
- centos7 1810 (59)
- 加载com加载项时运行错误 (67)
- php打乱数组顺序 (68)
- cad安装失败怎么解决 (58)
- 因文件头错误而不能打开怎么解决 (68)
- js判断字符串为空 (62)
- centos查看端口 (64)