由于请求的竞态问题,前端仔喜提了一个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包是一个用于机器学习的强大工具包。它提供了一种简单且灵活的方式来执行超参数调整。 生存网络是一种用于生存分析的模型,常用在医学和生物学领域。生存分析是一种统计方法,用于研究事件发生的时间和相关因素对事件发生的影响。生存网络可以用来预测个体在给…...
黑丝空姐-造相Z-Turbo场景应用:为你的内容创作提供无限灵感
黑丝空姐-造相Z-Turbo场景应用:为你的内容创作提供无限灵感 1. 镜像概述与核心能力 黑丝空姐-造相Z-Turbo是一款基于Xinference部署的文生图模型服务,通过gradio提供直观的交互界面。该镜像专注于生成特定风格的视觉内容,为创意工作者提供高…...
多进程和多线程的特点和区别
小编觉得,多进程和多线程的差异主要体现在以下三个方面: 1. 资源隔离 多线程属于同一进程,共享进程的堆内存和全局变量,因此线程间可以直接访问彼此共享的数据。但需要注意的是,每个线程也拥有自己私有的栈空间&…...
千问3.5-9B模型切换指南:OpenClaw多模型动态调用
千问3.5-9B模型切换指南:OpenClaw多模型动态调用 1. 为什么需要多模型动态调用 上周我尝试用OpenClaw自动整理电脑里积压的300多份PDF文档时,遇到了一个有趣的现象:处理简单文件重命名任务时,轻量级模型响应飞快;但遇…...
OpenClaw故障模拟:Qwen3-14b_int4_awq异常输入处理与恢复机制
OpenClaw故障模拟:Qwen3-14b_int4_awq异常输入处理与恢复机制 1. 为什么需要主动制造故障 去年冬天的一个深夜,我的OpenClaw自动化流程突然中断了。当时它正在帮我整理一批技术文档,却在处理某个特殊字符时直接"卡死"。这次经历让…...
别再只用手动调参了!用ArcGIS的Geostatistical Analyst工具包自动优化克里金插值参数
解锁ArcGIS隐藏技能:用Geostatistical Analyst实现克里金插值参数智能优化 当你在深夜盯着屏幕上半变异函数模型的参数犹豫不决时,是否想过让软件替你做出更科学的选择?克里金插值作为地统计学的黄金标准,其精度高度依赖于半变异函…...
AtCoder Beginner Contest 429
【赛时五题】AtCoder Beginner Contest 429 https://www.bilibili.com/video/BV1gXsZz8ELL/ 【赛时6题】AtCoder Beginner Contest 429 https://www.bilibili.com/video/BV1gXsZz8EZQ/ Atcoder Beginner Contest 429 https://www.bilibili.com/video/BV1SosZzdENX/ https://blo…...
从CPython 3.12到3.14:我们逆向了217个AOT相关PR,提炼出6个决定编译成功率的核心宏定义(含Py_BUILD_CORE_MODULE与Py_LIMITED_API冲突解决方案)
第一章:Python 原生 AOT 编译方案 2026 高级开发技巧Python 社区在 2026 年迎来关键演进:CPython 官方正式集成原生 Ahead-of-Time(AOT)编译能力,无需依赖第三方运行时或 JIT 层即可生成平台专用的静态可执行文件。该特…...
Java集合判空全攻略:从原生方法到Apache Commons工具类对比
Java集合判空全攻略:从原生方法到Apache Commons工具类对比 在Java开发中,集合判空是最基础却又最容易出错的环节之一。一个看似简单的判空操作,背后可能隐藏着NPE风险、性能损耗甚至逻辑漏洞。本文将深入剖析Java原生判空方法与Apache Commo…...
从Java全栈工程师视角看Web开发的实战与思考
从Java全栈工程师视角看Web开发的实战与思考 面试现场:一次真实的技术对话 面试官:你好,我是今天的面试官,很高兴见到你。请先简单介绍一下自己。 应聘者:你好,我叫李明,28岁,本科学…...
3步搞定iOS微信聊天记录完整导出:WeChatExporter终极指南
3步搞定iOS微信聊天记录完整导出:WeChatExporter终极指南 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 还在为无法备份微信聊天记录而烦恼吗?微…...
