如何封装一个可取消的 HTTP 请求?

前言

你可能会好奇什么样的场景会需要取消 HTTP 请求呢?

确实在实际的项目开发中,可能会很少有这样的需求,但是不代表没有,比如:

假如要实现上述这个公告栏,每点击一个 tab 按钮就会切换展示容器容器中的内容,但是由于这是三个 tab 按钮对应展示容器和信息条目结构样式都一致,于是为了 复用这个结构,将展示容器和其中的信息条目都作为三个 tab 按钮对应展示效果,只是点击每个 tab 按钮后 发送请求 后得到的数据不同.

正常情况下 tab1 对应的数据在初始化时进行展示,但是现在还存在一个问题,那就是如果点击完 tab2 并且已经发送了请求获取数据,假设这个请求需要 30s 后才响应回来,由于用户比较着急于是直接点了 tab3 ,此时 tab3 对应的接口已经发送出去但是未响应,而此时 tab2 对应的接口响应回来了,就会导致展示容器中的信息条目展现了错误的数据.

现在你应该知道,为什么需要一个可取消的 HTTP 请求了吧!!

准备工作

为了避免 端口、协议、IP 等不一致产生的跨域问题,这里就直接使用 express 启动一个本地服务,并返回测试页面 index.html,目录结构如下:

image.png

  • 其中最外层的 serverv.js 就是本地服务的代码,内容很简单:
    const path = require('path')
    const express = require('express')
    const app = express()// 返回静态资源
    app.use(express.static(path.join(__dirname, 'src')))// 处理 api 请求接口
    app.get('/message', function (req, res) {setTimeout(()=>{res.send('Hello World')} , 1000);
    });app.listen(3000, (error) => {if (error) {console.error("server error:", error);return}console.log("server runing at port 3000 ...");
    })
    
  • src/index.html 是测试页面,里面引入 src/request.js 中封装的请求方法
  • src/request.js 简单的封装了一些请求方法,包含 XMLHttpRequestfetchaxios 三种方式,内容如下:
    // xhrRequest
    function xhrRequest({method = 'get',url,params = null,async = true,success,
    }) {success = typeof success === 'function' ? success : () => {}const xhr = new XMLHttpRequest()xhr.open(method, url, async)xhr.onreadystatechange = () => {if (xhr.readyState == 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {success(xhr.responseText)} else {console.log('Request was unsuccessful: ' + xhr.status)}}}method === 'post' ? xhr.send(params || null) : xhr.send()return () => {console.log(`当前请求已被取消,url:`, url)xhr.abort()}
    }// fetchRequest
    function fetchRequest({ method = 'GET', url, params }) {let abortController = new AbortController()let patload = {method: method,signal: abortController.signal,}if (method === 'POST') patload.body = JSON.stringify(params)return {abort: () => {console.log(`当前请求已被取消,url:`, url)abortController.abort()},result: fetch(url, patload),}
    }// axiosRequest
    window.axiosCancelArr = []
    let Axios = null
    function axiosRequest() {if (Axios) return AxiosAxios = axios.create({baseURL: '',timeout: 10000,})//请求前拦截Axios.interceptors.request.use((config) => {// 添加取消标记config.cancelToken = new axios.CancelToken((cancel) => {window.axiosCancelArr.push({url: config.url, cancel})})return config},(error) => {return Promise.reject(error)},)//请求后返回数据拦截Axios.interceptors.response.use((res) => {return res},(res) => {return Promise.reject(res)},)return Axios
    }
    

原生方法取消请求

XMLHttpRequest —— (new XMLHttpRequest()).abort()

下面的图示,演示了 请求成功请求被取消 时的两种表现:

以下是在测试页面 index.html 中的代码,对应 js 代码在准备工作部分:

  <div><button onclick="send()">send request</button><button onclick="cancel()">cancel request</button></div><script src="./request.js"></script><script>let cancel = () => {}function send(){console.log("正在发送 xhr 请求...")// 获取取消请求的方法cancel = xhrRequest({url:'/message',success(data){console.log("接收数据:", data)}})}<script>

fetch —— (new AbortController()).abort()

下面的图示,演示了 请求成功请求被取消 时的两种表现:

以下是在测试页面 index.html 中的代码,对应 js 代码在准备工作部分:

<div><button onclick="send()">send request</button><button onclick="cancel()">cancel request</button>
</div><script src="./request.js"></script>
<script>let cancel = () => {}// fetchfunction send() {console.log('正在发送 fetch 请求...')const { result, abort } = fetchRequest({url: '/message',})cancel = abortresult.then((data) => {console.log('接收数据:', data)}).catch((error) => {console.log('fetch error:', error)})}</script>

axios —— new axios.CancelToken((cancel) => {}))

下面的图示,演示了 请求成功请求被取消 时的两种表现:

以下是在测试页面 index.html 中的代码,对应 js 代码在准备工作部分:

<div><button onclick="send()">send request</button><button onclick="cancel()">cancel request</button>
</div><script src="./request.js"></script>
<script>let cancel = () => {}// axiosconst AxiosInstance = axiosRequest()function send() {console.log('正在发 axios 送请求...')AxiosInstance.get('/message').then((res) => {console.log('接收数据:', res)}).catch((error) => {console.log('axios error:', error)})}cancel = () => {console.log('axiosCancel = ', window.axiosCancel)window.axiosCancelArr.forEach((item) => {item.cancel()})}
</script>

实现自定义方法 " 取消请求 "

注意这里的 “请求方法” 是包含引号的,也就是说并不是像原生方法那样真的把已发送出去的请求进行取消,但是可以通过其他方式不在接收之前请求响应的数据,转而使用最新接口响应的数据,从侧面实现避免旧接口响应造成的干扰.

如果要自定义实现 “取消请求” 方法,最先想到的应该是 promise,因为 promise 有一个重要的特性就是:状态一旦从 pending -> fullfilled 或 pending -> rejected 的变更,之后就无法在进行更改.

既然如此,我们就可以在接口响应时通过 promise.resolve() 进行返回,一旦需要 “取消请求” 就可以先于接口响应时先更改对应 promise 的状态,从而抛弃上一次接口响应的数据.

request.js 中代码如下:

// xhrRequestPromise
window.cancelXhrRequestArr = []
function xhrRequestPromise({method = 'get',url,params = null,async = true,
}) {return new Promise((resolve, reject) => {cancelXhrRequestArr.push({ reject, url })success = typeof success === 'function' ? success : () => {}const xhr = new XMLHttpRequest()xhr.open(method, url, async)xhr.onreadystatechange = () => {if (xhr.readyState == 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {resolve(xhr.responseText)} else {console.log('Request was unsuccessful: ' + xhr.status)}}}method === 'post' ? xhr.send(params || null) : xhr.send()})
}

测试页面 index.html 代码如下:

<div><button onclick="send()">send request</button><button onclick="cancel()">cancel request</button>
</div><script src="./request.js"></script>
<script>let cancel = () => {cancelXhrRequestArr.forEach(({reject, url}) => {reject(`abandon request url:${url}`)})}// xhrRequestPromisefunction send() {console.log('正在发送 xhr 请求...')const xhrp = xhrRequestPromise({ url: '/message' }).then((res) => {console.log('接收数据:', res)}).catch((error) => {console.log('xhrRequest error:', error)})}</script>

最后

上面通过 XMLHttpRequestfetchaxios 三种方式来演示了如何取消请求,同时也通过 promise 实现了自定义 “取消请求” 的方法,简单来说,通过原生方法就是把这个请求给取消了,自定义实现方法就是把将旧数据直接抛弃不适用,明白了这一点其实通过其他的方式实现也可以.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/58290.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图的最短路径算法-迪杰斯特拉(Dijkstra)算法与弗洛伊德(Frolyd)算法(更新中)

一、最短路径算法&#xff08;Shortest Path&#xff09; 最短路径问题是图论研究中的一个经典算法问题&#xff0c;旨在寻找图&#xff08;由结点和路径组成的&#xff09;中两结点之间的最短路径。 最短路径不一定是经过边最少的路径&#xff0c;但在这些最短路径中&#x…

JSON文件转YOLO文件示例

文章目录 前言一、步骤指南二、代码实现1.类别名称到ID的映射2.边界框转换函数3.JSON解码函数4.主程序 前言 将JSON标注文件转换为YOLO格式通常涉及从JSON文件中提取图像尺寸、对象类别和边界框坐标&#xff0c;并将这些信息格式化为YOLO格式所需的格式。YOLO格式通常要求每行…

ubuntu编译ffmpeg

配置 运行环境&#xff1a;vmware ubuntu 20.04 时间&#xff1a;2024年10月24日 权限问题&#xff1a;由于ubuntu权限问题 建议使用root权限编译&#xff0c;且~是根据用户组来进行定位的。 环境配置更新 cd ~ && \ mkdir ffmpeg_sources ffmpeg_build bin &…

EasyExcel自定义下拉注解的三种实现方式

文章目录 一、简介二、关键组件1、ExcelSelected注解2、ExcelDynamicSelect接口&#xff08;仅用于方式二&#xff09;3、ExcelSelectedResolve类4、SelectedSheetWriteHandler类 三、实际应用总结 一、简介 在使用EasyExcel设置下拉数据时&#xff0c;每次都要创建一个SheetWr…

【vs2022】windows可用的依赖预编译库

ffmpeg 、x264 、x265 等。obs是基于qt6+vs2022+64bit obs的官网传统构建已经不用了obs的s2022构建OBS Deps Build 2024-09-12FFmpeg4.4 库,x64 可用。

每天五分钟深度学习pytoroch:基于pytorch搭建逻辑回归算法模型

本文重点 前面我们学习了线性回归模型的搭建,无论是基于pytorch还是不基于pytorch,以上的模型都是回归模型,本文我们将使用pytorch搭建逻辑回归模型,逻辑回归模型是一个经典的分类问题。 模型搭建 class LogisticRegression(nn.Module) : def __init__(self) :super (Lo…

嵌入式软件 Bug 排查与调试技巧

目录 1、准备工作 2、打印调试 实现步骤 注意事项 3、断点调试 4、观察点调试 5、远程调试 6、内存分析 内存泄漏检测 栈溢出检测 7、异常处理 8、性能分析 9、逻辑分析仪 10、示波器 11、常见bug类型 12、调试策略 1、准备工作 硬件工具准备 调试器:例如 J - …

玩转Docker | 使用Docker部署推箱子网页小游戏

玩转Docker | 使用Docker部署推箱子网页小游戏 一、项目介绍项目简介项目预览 二、系统要求环境要求环境检查Docker版本检查检查操作系统版本 三、部署推箱子网页小游戏下载镜像创建容器检查容器状态检查服务端口安全设置 四、访问推箱子网页小游戏五、总结 一、项目介绍 项目…

什么是服务器?服务器与客户端的关系?本地方访问不了网址与服务器访问不了是什么意思?有何区别

服务器是一种高性能的计算机&#xff0c;它通过网络为其他计算机&#xff08;称为客户端&#xff09;提供服务。这些服务可以包括文件存储、打印服务、数据库服务或运行应用程序等。服务器通常具有强大的处理器、大量的内存和大容量的存储空间&#xff0c;以便能够处理多个客户…

Iperius Backup(数据备份软件) v8.3.0 中文免费版

下载&#xff1a; 【1】https://pan.quark.cn/s/19ef716c02d5 【2】https://drive.uc.cn/s/197acba8d8d94?public1 Iperius Backup是一款专业的备份还原软件&#xff0c;功能强大&#xff0c;支持DAT备份、LTO备份、NAS备份、磁带备份、RDX驱动器、USB备份&#xff0c;满足用…

SOES(EtherCAT)从站API梳理

1. void ESC_config (esc_cfg_t * cfg); 功能&#xff1a;配置EtherCAT从站。参数&#xff1a;esc_cfg_t *cfg 指向配置结构体的指针&#xff0c;该结构体包含从站的配置参数。解释&#xff1a;该函数用于初始化或更新从站的配置&#xff0c;如通信参数、同步管理器设置等。 …

Java Lock Condition 总结

前言 相关系列 《Java & Lock & 目录》&#xff08;持续更新&#xff09;《Java & Lock & Condition & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Java & Lock & Condition & 总结》&#xff08;学习总结/最新最准…

K8S测试pod内存和CPU资源不足

只设置requests参数 mysql主从pod启动后监控 读压测之后 同时设置limits和requests&#xff0c;只调低内存值 监控 压力测试 同时设置limits和requests&#xff0c;只调低CPU值 初始状态 开始压测 结论 对于CPU&#xff0c;如果pod中服务使用CPU超过设置的limits&…

谷歌云GCP基础概念讲解

概览 云的基础是虚拟化&#xff1a;服务器&#xff0c;存储&#xff0c;网络。服务器是远程计算机的逻辑分区。存储是物理硬盘的逻辑划分。网络则是虚拟私有云。 谷歌是唯一一个拥有全球私有基础设施的公司&#xff1b;他们的谷歌云基础设施没有任何一部分通过公共互联网。换句…

绿盟科技发布三季度报告,收入略增,亏损收窄,经营性净现金流同比翻倍

10月30日&#xff0c;绿盟科技发布2024年三季度报告。2024年公司前三季度实现营业收入12.74亿元&#xff0c;同比增长5.57%&#xff1b;毛利率59.50%&#xff0c;同比增长4.76个百分点&#xff1b;期间费用总额同比下降7.68%&#xff1b;公司实现归属于上市公司股东的净利润-3.…

【云原生】云原生后端详解:架构与实践

目录 引言一、云原生后端的核心概念1.1 微服务架构1.2 容器化1.3 可编排性1.4 弹性和可伸缩性 二、云原生后端的架构示意图三、云原生后端的最佳实践3.1 使用服务网格3.2 监控与日志管理3.3 CI/CD 流水线3.4 安全性 总结参考资料 引言 随着云计算的迅猛发展&#xff0c;云原生…

Python 爬虫的寻宝大冒险:如何捕获 API 数据的宝藏

在这个信息爆炸的数字时代&#xff0c;数据就像是隐藏在网络深处的宝藏&#xff0c;等待着勇敢的探险家去发现。今天&#xff0c;我们要讲述的是如何成为一名 Python 爬虫探险家&#xff0c;装备你的代码工具&#xff0c;深入 API 的迷宫&#xff0c;捕获那些珍贵的数据宝藏。 …

C++-类与对象总结

const函数声明 1. 修饰成员函数&#xff0c;不会改变成员变量&#xff1a; - a function b const (c){}: in member function means, all member properties in the function cannot be modified. 2.修饰形参&#xff0c;输入参数在函数中不会被更改&#xff0c;提高程序的…

《近似线性可分支持向量机的原理推导》 拉格朗日函数 公式解析

本文是将文章《近似线性可分支持向量机的原理推导》中的公式单独拿出来做一个详细的解析&#xff0c;便于初学者更好的理解。 公式 9-41 解释&#xff1a; L ( w , b , ξ , α , μ ) 1 2 ∥ w ∥ 2 C ∑ i 1 N ξ i − ∑ i 1 N α i ( y i ( w T x i b ) − ( 1 − ξ …

【云原生】云原生后端:安全性最佳实践

目录 引言一、身份管理1.1 身份验证1.2 身份授权 二、数据加密2.1 数据静态加密2.2 数据传输加密2.3 密钥管理 三、网络安全3.1 网络隔离3.2 防火墙与入侵检测3.3 安全组与网络访问控制列表 (NACL) 结论 引言 在云原生架构中&#xff0c;安全性是一个至关重要的考量。随着应用…