如何封装一个可取消的 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…

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…

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

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

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;满足用…

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

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

谷歌云GCP基础概念讲解

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

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

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

C++,STL 051(24.10.28)

内容 1.map容器的构造函数。 2.map容器的赋值操作。 运行代码 #include <iostream> #include <map>using namespace std;void printMap(map<int, int> &m) {for (map<int, int>::iterator it m.begin(); it ! m.end(); it){cout << &quo…

《链表篇》---环形链表II(返回节点)

题目传送门 方法一&#xff1a;哈希表&#xff08;与环形链表类似&#xff09; 很容易就可以找到链表的相交位置。 public class Solution {public ListNode detectCycle(ListNode head) {if(head null || head.next null){return null;}Set<ListNode> visited new Ha…

从0开始深度学习(17)——数值稳定性和模型初始化

在每次训练之前&#xff0c;都会对模型的参数进行初始化&#xff0c;初始化方案的选择在神经网络学习中起着举足轻重的作用&#xff0c; 它对保持数值稳定性至关重要。 我们选择哪个函数以及如何初始化参数可以决定优化算法收敛的速度有多快。 糟糕选择可能会导致我们在训练时遇…

重学SpringBoot3-怎样优雅停机

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-怎样优雅停机 1. 什么是优雅停机&#xff1f;2. Spring Boot 3 优雅停机的配置3. Tomcat 和 Reactor Netty 的优雅停机机制3.1 Tomcat 优雅停机3.2 Reac…

【C++初阶】模版入门看这一篇就够了

文章目录 1. 泛型编程2. 函数模板2. 1 函数模板概念2. 2 函数模板格式2. 3 函数模板的原理2. 4 函数模板的实例化2. 5 模板参数的匹配原则2. 6 补充&#xff1a;使用调试功能观察函数调用 3. 类模板3 .1 类模板的定义格式3. 2 类模板的实例化 1. 泛型编程 在C语言中&#xff0…

动态规划-子序列问题——1027.最长等差数列

1.题目解析 题目来源&#xff1a;1027.最长递增子序列——力扣 测试用例 2.算法原理 1.状态表示 等差数列至少是三个数&#xff0c;所以一维数组显然无法满足状态表示&#xff0c;所以需要开辟一个二维数组来表示&#xff0c;二维数组的两个下标分别是倒数第一与倒数第二个数 …

【JSON相关漏洞(Hijacking+Injection)挖掘技巧及实战案例全汇总】

JSON相关漏洞&#xff08;HijackingInjection&#xff09;挖掘技巧及实战案例全汇总 本文一是在为测试过程中遇到json返回格式时提供测试思路&#xff0c;二是几乎所有国内的资料都混淆了json和jsonp的区别——这是两种技术&#xff1b;以及json和jsonp hijacking的区别——这…

基于知识图谱的设备问答系统

你是不是还在为毕业设计苦恼&#xff1f;今天我就来给大家分享一款基于知识图谱的设备问答系统&#xff0c;不仅技术领先&#xff0c;而且非常适合用作毕业设计项目&#xff0c;帮助你轻松通过&#xff01; 项目简介 这个设备问答系统基于Django&#xff08;后端框架&#xf…

从零开始的 vue项目部署到服务器详细步骤(vue项目build打包+nginx部署+配置ssl证书)

从零开始的 vue项目部署到服务器详细步骤&#xff08;vue项目build打包nginx部署配置ssl证书&#xff09; 文章目录 从零开始的 vue项目部署到服务器详细步骤&#xff08;vue项目build打包nginx部署配置ssl证书&#xff09;一、前言二、vue项目部署前配置1、vite.config.js 增加…

ubuntu内核更新导致显卡驱动掉的解决办法

方法1&#xff0c;DKMS指定内核版本 用第一个就行 1&#xff0c;借鉴别人博客解决方法 2&#xff0c;借鉴别人博客解决方法 方法2&#xff0c;删除多于内核的方法 系统版本&#xff1a;ubuntu20.24 这个方法是下下策&#xff0c;如果重装驱动还是不行&#xff0c;就删内核在…