当前位置: 首页 > news >正文

由于请求的竞态问题,前端仔喜提了一个bug

在平常的开发过程中,你可能会遇到这样一个bug。

测试:我在测一个输入框搜索的功能时,告诉你通过输入框输入的内容,和最终通过输入内容搜索出来的结果对不上。

前端:我是通过调用后端接口拿到的数据,这明显是后端返回的结果有问题啊,找后端去!

后端:通过Postman一通自测后说道,结果没问题啊!找前端去!

前端:我来试试看!一顿输入后发现,没问题啊,这bug我复现不出来啊!

测试:这个bug是偶现的!

前端:一通排查后发现,bug的直接原因是网络问题,根本原因是竞态问题,导致的数据不一致(喜提一个有意思的bug)。

一、bug原因

举个例子:

  1. 你先输入1,发起请求A,此时请求参数为{ searchKey: '1' };
  2. 你再输入2,发起请求B,此时请求参数为{ searchKey: '12' };

此时由于网络原因,导致先发起的请求A的响应结果比请求B的慢

  1. 拿到响应B的结果,此时页面先渲染B的响应结果;
  2. 拿到响应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来实现请求的取消,具体步骤如下:

  1. 先通过new AbortController创建一个实例,比如叫controller
  2. 通过controller.signal拿到一个信号,然后再发起fetch请求的时候带上这个信号,然后这个请求就与这个信号关联在一起了。
  3. 通过第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来取消请求的,其步骤如下:

  1. 通过axios上的静态属性CancelToken直接先拿到CancelToken,
  2. 调用CancelToken.source()方法,拿到source,
  3. 发请求时,通过带上source.token,将请求和source相关联,
  4. 通过第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方法来实现的。

四、应用场景

那么取消请求有哪些应用场景呢?

  1. 连续搜索:那就是上面提到的bug,在连续发起多个请求后,就可能会出现竞态问题,引发bug,
  2. 大文件上传:比如在做一个大文件上传功能时,用户上传文件后,页面显示了实时进度条以及取消上传按钮,假如用户此时点击了取消按钮,就应该把已经发送但未拿到响应的请求取消掉。

五、小结

上面主要介绍了通过xhrfetch以及axios发起的请求如何取消,xhr是通过abort方法来取消请求,fetch则是借助了AbortController类来实现,而axios虽然表面是借助了CancelToken来实现,实际底层还是调用xhr.abort()方法来实现的。

http://www.lryc.cn/news/525669.html

相关文章:

  • 【Day25 LeetCode】贪心Ⅲ
  • 蓝桥杯练习日常|递归-进制转换
  • AI Agent:深度解析与未来展望
  • 《SwinIR:使用Swin-Transformer图像恢复》学习笔记
  • 如何在Nginx服务器上配置访问静态文件目录并提供文件下载功能
  • ansible自动化运维实战--script、unarchive和shell模块(6)
  • 理解深度学习pytorch框架中的线性层
  • 电路研究9.2——合宙Air780EP使用AT指令
  • Qt数据库相关操作
  • 2025-01-22 Unity Editor 1 —— MenuItem 入门
  • 解锁C#编程新姿势:Z.ExtensionMethods入门秘籍
  • 不使用 JS 纯 CSS 获取屏幕宽高
  • Node.js NativeAddon 构建工具:node-gyp 安装与配置完全指南
  • 【ARTS】【LeetCode-704】二分查找算法
  • Vue.js 配置路由:基本的路由匹配
  • 鸿蒙(HarmonyOS)Json格式转实体对象(2)
  • 代码随想录 栈与队列 test 6
  • 动手学深度学习2025.1.23
  • 生存网络与mlr3proba
  • C#与AI的共同发展
  • 2000-2020年各省第二产业增加值数据
  • 【MySQL】 库的操作
  • docker 启动镜像命令集合
  • 微信小程序获取位置服务
  • Docker Load后存储的镜像及更改镜像存储目录的方法
  • Langchain本地知识库部署
  • java基础学习——jdbc基础知识详细介绍
  • 联想电脑怎么设置u盘启动_联想电脑设置u盘启动方法(支持新旧机型)
  • C# 解析 HTML 实战指南
  • 光谱相机在智能冰箱的应用原理与优势