由于请求的竞态问题,前端仔喜提了一个bug
在平常的开发过程中,你可能会遇到这样一个bug。
测试:我在测一个输入框搜索的功能时,告诉你通过输入框输入的内容,和最终通过输入内容搜索出来的结果对不上。
前端:我是通过调用后端接口拿到的数据,这明显是后端返回的结果有问题啊,找后端去!
后端:通过Postman
一通自测后说道,结果没问题啊!找前端去!
前端:我来试试看!一顿输入后发现,没问题啊,这bug
我复现不出来啊!
测试:这个bug是偶现的!
前端:一通排查后发现,bug的直接原因是网络问题,根本原因是竞态问题,导致的数据不一致(喜提一个有意思的bug)。
一、bug原因
举个例子:
- 你先输入1,发起请求A,此时请求参数为
{ searchKey: '1' }
; - 你再输入2,发起请求B,此时请求参数为
{ searchKey: '12' }
;
此时由于网络原因,导致先发起的请求A的响应结果比请求B的慢
- 拿到响应B的结果,此时页面先渲染B的响应结果;
- 拿到响应A的结果,此时页面再渲染A的响应结果。
所以最后就会发现,你输入的结果,跟搜索出来的结果对不上。
二、解决方案
要解决它,就需要了解一个知识点,那就是如何取消请求
。我在发起请求B的时候,把请求B取消不就搞定了么!
三、如何取消请求?
1. 原生XMLHttpRequest
如果用的JavaScript
原生的XMLHttpRequest
发起的请求,可以通过调用abort
方法来取消请求。
var xhr = new XMLHttpRequest();
var url = 'http://localhost:3000/data';
var timer;xhr.open('GET', url, true);xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {clearTimeout(timer); // 如果在300ms内收到响应,则清除定时器var response = JSON.parse(xhr.responseText);console.log(response);} else {console.log('Error: ' + xhr.status);}
};// 设置定时器,在300ms后取消请求
timer = setTimeout(function() {xhr.abort();console.log('Request aborted due to timeout');
}, 300);// 发起请求
xhr.send();
我这里利用XMLHttpRequest
发起了一个请求,如果300ms
后未拿到响应,我便会调用abort
方法取消请求。
2. fetch
通过fetch
发起的请求,需要通过AbortController
来实现请求的取消,具体步骤如下:
- 先通过
new AbortController
创建一个实例,比如叫controller
, - 通过
controller.signal
拿到一个信号
,然后再发起fetch
请求的时候带上这个信号
,然后这个请求就与这个信号
关联在一起了。 - 通过第2步的关联之后,可以随时通过调用
controller.abort()
取消请求。
具体代码如下,通过fetch
发起请求,还是在300ms
后未拿到响应便取消请求:
// 创建一个 AbortController 实例
const controller = new AbortController();
const signal = controller.signal;// 设置取消请求的定时器
const timeout = setTimeout(() => {controller.abort();console.log('Request aborted due to timeout');
}, 300); // 300ms// 发起 Fetch 请求
fetch('http://localhost:3000/data', { signal }).then(response => {clearTimeout(timeout); // 清除定时器return response.json();}).then(data => {console.log(data);}).catch(error => {if (error.name === 'AbortError') {console.log('Fetch aborted');} else {console.error('Error:', error);}});
3、axios
其实平常开发中,发请求最常用的方式还是axios
,它是对原生XMLHttpRequest
的一个封装,让我们用起来更爽。
它是通过CancelToken
来取消请求的,其步骤如下:
- 通过
axios
上的静态属性CancelToken
直接先拿到CancelToken
, - 调用
CancelToken.source()
方法,拿到source
, - 发请求时,通过带上
source.token
,将请求和source
相关联, - 通过第2步的关联之后,可以随时通过调用
source.cancel
取消请求。
完整代码如下:
// 创建一个 CancelToken.source
const CancelToken = axios.CancelToken;
const source = CancelToken.source();// 发起 Axios 请求并传入 cancel token
axios.get('http://localhost:3000/data', {cancelToken: source.token
})
.then(response => {console.log(response.data);
})
.catch(function (thrown) {if (axios.isCancel(thrown)) {console.log('Request canceled', thrown.message);} else {console.error('Error:', thrown);}
});// 在需要的时候取消请求
setTimeout(() => {source.cancel('Request canceled due to timeout');
}, 300); // 300ms
axios底层也是通过
abort
方法来实现的。
四、应用场景
那么取消请求有哪些应用场景呢?
- 连续搜索:那就是上面提到的bug,在连续发起多个请求后,就可能会出现竞态问题,引发bug,
- 大文件上传:比如在做一个大文件上传功能时,用户上传文件后,页面显示了实时进度条以及取消上传按钮,假如用户此时点击了取消按钮,就应该把已经发送但未拿到响应的请求取消掉。
五、小结
上面主要介绍了通过xhr
、fetch
以及axios
发起的请求如何取消,xhr
是通过abort
方法来取消请求,fetch
则是借助了AbortController
类来实现,而axios
虽然表面是借助了CancelToken
来实现,实际底层还是调用xhr.abort()
方法来实现的。
相关文章:
由于请求的竞态问题,前端仔喜提了一个bug
在平常的开发过程中,你可能会遇到这样一个bug。 测试:我在测一个输入框搜索的功能时,告诉你通过输入框输入的内容,和最终通过输入内容搜索出来的结果对不上。 前端:我是通过调用后端接口拿到的数据,这明显…...
【Day25 LeetCode】贪心Ⅲ
一、贪心Ⅲ 1、加油站 134 这道题直接想法是采用二重循环暴力搜索,简单粗暴但是会超时,是因为以每个点为起点最坏的情况可能都要遍历完全部的序列,有大量重复的操作,那有没有优化的地方呢?有一个结论:如果…...

蓝桥杯练习日常|递归-进制转换
未完待续,,,,,, 目录 蓝桥云课760数的计算 一、递归 题目: 我的解题代码: 二、进制转换 任意进制转十进制: 十进制转换为其他进制: 进制蓝桥杯题目…...

AI Agent:深度解析与未来展望
一、AI Agent的前世:从概念到萌芽 (一)早期探索 AI Agent的概念可以追溯到20世纪50年代,早期的AI研究主要集中在简单的规则系统上,这些系统的行为是确定性的,输出由输入决定。随着时间的推移,…...

《SwinIR:使用Swin-Transformer图像恢复》学习笔记
paper:2108.10257 GitHub:GitHub - JingyunLiang/SwinIR: SwinIR: 使用 Swin Transformer 进行图像修复 (官方仓库) 目录 摘要 1、Introduction 2、Related Work 2.1 图像修复 2.2 视觉Transformer…...
如何在Nginx服务器上配置访问静态文件目录并提供文件下载功能
引言 在搭建网站的过程中,我们经常需要让访客通过URL直接访问或下载存储在服务器特定目录下的静态文件。本文将详细介绍如何在Nginx服务器环境中配置一个名为"download"的文件目录,以便用户能够通过浏览器访问并下载其中的手册和其他文档。 …...

ansible自动化运维实战--script、unarchive和shell模块(6)
文章目录 一、script模块1.1、功能1.2、常用参数1.3、举例 二、unarchive模块2.1、功能2.2、常用参数2.3、举例 三、shell模块3.1、功能3.2、常用参数3.3、举例 一、script模块 1.1、功能 Ansible 的 script 模块允许你在远程主机上运行本地的脚本文件,其提供了一…...
理解深度学习pytorch框架中的线性层
文章目录 1. 数学角度: y W x b \displaystyle y W\,x b yWxb示例 2. 编程实现角度: y x W T b \displaystyle y x\,W^T b yxWTb3. 常见错误与易混点解析4. 小结参考链接 在神经网络或机器学习的线性层(Linear Layer / Fully Connect…...

电路研究9.2——合宙Air780EP使用AT指令
这里正式研究AT指令的学习了,之前只是接触的AT指令,这里则是深入分析AT指令了。 软件的开发方式: AT:MCU 做主控,MCU 发 AT 命令给模组的开发方式,模组仅提供标准的 AT 固件, 所有的业务控制逻辑…...
Qt数据库相关操作
目录 一、前言 二、类与接口介绍 1.连接管理类 2.数据操作类 3.数据模型类 4.其它类 三、主要操作流程 1.示例 2.绑定参数 3.事务操作 一、前言 要在Qt中操作数据库,首先要安装对应的数据库,还要确保安装了Qt SQL模块。使用MySQL时࿰…...

2025-01-22 Unity Editor 1 —— MenuItem 入门
文章目录 1 Editor 文件夹2 MenuItem3 使用示例3.1 打开网址3.2 打开文件夹3.3 Menu Toggle3.4 Menu 代码复用3.5 MenuItem 激活与失活4 代码示例 1 Editor 文件夹 Editor 文件夹是 Unity 中的特殊文件夹,Unity 中所有编辑器相关的脚本都需要放置在其中…...
解锁C#编程新姿势:Z.ExtensionMethods入门秘籍
一、引言 在 C# 的开发旅程中,我们常常会遇到各种重复性高、复杂度低的任务,这些任务虽然基础,但却占据了我们大量的开发时间。比如处理字符串时,经常需要进行非空判断、格式转换;操作日期时间时,计算某个…...
不使用 JS 纯 CSS 获取屏幕宽高
前言 在现代前端开发中,获取屏幕的宽度和高度通常依赖于 JavaScript。然而现代 CSS 也可以获取到屏幕的宽高,通过自定义属性(CSS Variables)和一些数学函数来实现这一目标。本文将详细解析如何使用 CSS 的 property 规则和一些数…...
Node.js NativeAddon 构建工具:node-gyp 安装与配置完全指南
Node.js NativeAddon 构建工具:node-gyp 安装与配置完全指南 node-gyp Node.js native addon build tool [这里是图片001] 项目地址: https://gitcode.com/gh_mirrors/no/node-gyp 项目基础介绍及主要编程语言 Node.js NativeAddon 构建工具(node-gyp…...
【ARTS】【LeetCode-704】二分查找算法
目录 前言 什么是ARTS? 算法 力扣704题 二分查找 基本思想: 二分查找算法(递归的方式): 经典写法(找单值): 代码分析: 经典写法(找数组即多个返回值) 代码分析 经典题目 题目描述: 官方题解 深入思考 模版一 (相错终止/左闭右闭) 相等返回情形…...

Vue.js 配置路由:基本的路由匹配
Vue.js 配置路由:基本的路由匹配 在 Vue.js 应用中,Vue Router 是官方提供的路由管理器,用于在单页应用(SPA)中管理不同的视图。通过配置路由,应用可以根据 URL 的变化展示相应的组件。 基本的路由匹配是…...

鸿蒙(HarmonyOS)Json格式转实体对象(2)
下面是一个复杂的json体。 怎么把json转实体类,首先要定义类 import List from ohos.util.List export class InfoModel{msg: stringcars: List<Cars>code: numberpermissions: List<string>roles: List<string>user: User}class Cars{createBy:…...
代码随想录 栈与队列 test 6
239. 滑动窗口最大值 - 力扣(LeetCode) 每次只取窗口中最大值,这个最大值可能在后面的滑动中保持不变,而比最大值小的值且在最大值之前出现的值没必要保留,因此可以通过单调队列利用这个特性。 这个单调队列具有如下…...
动手学深度学习2025.1.23
一、预备知识 1.数据操作 (1)数据访问: 一个元素:[1,2] //行下标为1,列下标为2的元素 一行元素:[1,:] //行下标为1的所有元素 一列元素:[:,1] //列下标为1的所有元素 子区域:[…...

生存网络与mlr3proba
在R语言中,mlr3包是一个用于机器学习的强大工具包。它提供了一种简单且灵活的方式来执行超参数调整。 生存网络是一种用于生存分析的模型,常用在医学和生物学领域。生存分析是一种统计方法,用于研究事件发生的时间和相关因素对事件发生的影响。生存网络可以用来预测个体在给…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...