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

由于请求的竞态问题,前端仔喜提了一个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.kler.cn/a/516237.html

相关文章:

  • 亚马逊新店铺流量怎么提升?自养号测评新趋势
  • JAVA毕业设计210—基于Java+Springboot+vue3的中国历史文化街区管理系统(源代码+数据库)
  • leetcode 2920. 收集所有金币可获得的最大积分
  • LabVIEW 水电站厂内经济运行系统
  • 全球化趋势与中资企业出海背景
  • 202009 青少年软件编程等级考试C/C++ 二级真题答案及解析(电子学会)
  • idea修改模块名导致程序编译出错
  • 【全栈】SprintBoot+vue3迷你商城(3)
  • 谷粒商城——商品服务-三级分类
  • 无界云剪音频教程:提升视频质感
  • 【游戏设计原理】79 - 可变奖励
  • MySQL下载安装DataGrip可视化工具
  • 【GPON实战】7360局端和C300局端流量统计指令
  • 如何实现各种类型的进度条
  • npm、cnpm 、yarn、pnpm的优势点和缺点
  • Prometheus+grafana实践:Doris数据库的监控
  • 30V/3A降压DCDC转换器CP8335封装可适用汽车系统
  • pinctrl子系统
  • Matlab实现TCN-BiLSTM时间卷积神经网络结合双向长短期记忆神经网络多特征分类预测(附模型研究报告)
  • vscode 自用插件
  • 优选算法——哈希表
  • 【算法】经典博弈论问题——巴什博弈 python
  • 背包模型 多重背包问题 3
  • 【智能体系统AgentOS】核心二:工作流
  • LetsWave脑电数据简单时频分析及画图matlab(二)
  • 我与NVIDIA的故事