把网络限制在 3G 速度后,我发现了两个测试用例完全没捕捉到的正确性 Bug

内卷王脚本小子 高级 2小时前 572 浏览 6 点赞 约 2 分钟

直接给结论:不要在高速 WiFi 环境下盲信你的异步请求顺序。通过在 Chrome DevTools 中将网络限制设置为 3G(注意现在 Chrome 把原来的 Slow 3G 统一改名叫 3G 了,位置在 Slow 4G 和 Offline 之间),我发现即便代码通过了所有单元测试,在慢速网络下依然会出现严重的竞态条件,导致界面显示错误数据。

把网络限制在 3G 速度后,我发现了两个测试用例完全没捕捉到的正确性 Bug

搜索结果顺序错乱的竞态 Bug

我测试了一个标准的搜索框功能,代码逻辑非常简单,就是典型的请求-响应模式:

async function search(query) {
 const res = await fetch(`/api/search?q=${query}`);
 const data = await res.json();
 setResults(data.results);
}
把网络限制在 3G 速度后,我发现了两个测试用例完全没捕捉到的正确性 Bug

在低延迟的 WiFi 环境下,请求和响应几乎是同步的,顺序极少发生翻转,所以平时根本没意识到问题。但在 3G 模拟环境下,这个 Bug 很容易触发。

具体场景是:如果我快速输入 "apple",停顿一下,又迅速改为 "banana",此时 "apple" 和 "banana" 两个请求都在传输中。由于网络波动的随机性,或者服务器处理时间的差异,如果 "apple" 的响应比 "banana" 晚到,它就会直接覆盖掉当前屏幕上本该显示的 "banana" 结果。

为了在日志里精准复现这个现象,我在服务端手动设置了不同的延迟(这不是靠网络限制实现的,而是服务端强制延迟):给 "apple" 设置 3 秒延迟,给 "banana" 设置 200 毫秒。

把网络限制在 3G 速度后,我发现了两个测试用例完全没捕捉到的正确性 Bug

测试运行日志如下:

sent request for "apple"
sent request for "banana"
got response for "banana" after 210ms
got response for "apple" after 3001ms

结果非常明显,虽然 "banana" 是最后一次搜索,但因为 "apple" 的响应最后到达,界面最终显示的是 "apple" 的搜索结果。这证明了单纯依赖响应到达的先后顺序来更新 UI 是极其危险的,任何在生命周期内触发多次异步请求的组件都可能潜伏这种问题。

如何彻底解决这种顺序覆盖问题

把网络限制在 3G 速度后,我发现了两个测试用例完全没捕捉到的正确性 Bug

解决这个问题的核心在于:不要信任响应的到达顺序,而要记录请求的顺序。

最稳妥的办法是在请求时维护一个标识(比如时间戳或递增 ID),在 setResults 之前检查当前响应对应的请求是否依然是“最新的”那个。如果响应回来时,已经有更新的请求发出去了,那么旧请求的响应就应该被直接丢弃,而不是更新到状态里。

这次实操给我最大的启发是,很多所谓的“性能问题”其实是“正确性问题”。如果只在开发环境的极速网络下测试,你永远无法发现这些在真实世界(尤其是移动端弱网)中频繁发生的 Bug。

AI编程AI编程实战javascriptChrome DevToolsRace Condition
AI工具与大模型实操经验整理在Claude实战技巧汇总,有不少直接可参考的案例。

全部回复 (3)

内卷王调参侠 中级 2小时前

这种客套话真没营养,快告诉我你用哪个插件跑出来的?

0 回复
架构师老刘 中级 2小时前

3G 测试都能跑出 bug 真的离谱,我现在就想试试把网速限到 128kbps 看看能崩成啥样。

0 回复
技术宅Ray 初级 2小时前

这坑我踩过,当时被搞得心态爆炸。你这竞态是用 axios 拦截器处理的还是直接在组件里用 AbortController?

0 回复

发表回复

支持 Markdown 格式