以往在业务中请求接口的时候,总是先请求的先返回,请求顺序和返回顺序可以对上,根据数据渲染的页面也就自然没有问题。
但假如有这样一个场景:
一个页面有两个tab,暂且命名为tab1和tab2,tab中是一个表格,我们通过点击tab请求接口获取对应tab中的表格数据,初始时没有选中tab。点击tab1先请求,但是由于接口数据过多,响应较慢,这时用户点击了tab2开始了第二次请求,tab2的请求中接口数据少,响应很迅速,于是在第一次请求还响应的时候,第二次请求已经响应并完成处理。
这个过程中,页面表现为:在tab2中先展示第二次接口返回的表格数据,然后再展示第一次接口请求返回的数据,这就导致了tab2展示的其实是tab1的数据,于是一个bug在某位程序员手中不知不觉的诞生了…
如何解决这一问题呢?
以上场景的问题核心其实是异步请求竞态,也就是两个异步请求的响应顺序与发送顺序不一致,导致后渲染的数据覆盖了先需要的内容。解决这一问题的方法其实也简单,就是忽略过期请求。
那么如何忽略过期请求呢,其实就是为每一个请求配一个请求唯一标识,响应时基于这个标识去判断是否为最新请求,如果不是最新请求,就不去采用返回的结果了。
多说无益,上代码
// 标识,用于判断接口响应是否为最新
let requestId = 0;
// 请求
async fetchList () {
// requestId自加后赋值给currentId 作为当前请求的唯一标识
const currentId = ++requestId;
const res = await fetch('/api/list');
// 响应返回后进行判断,不是最新返回则不采用
if (currentId !== requestId) {
return;
} else {
// 相等再进行对于res的处理
}
}
为什么能解决异步请求竞态的问题呢?
假设短时间内连续触发两次 fetchList():
- 第一次调用:currentId = 1,requestId 变为 1,发送请求 1。
- 第二次调用:currentId = 2,requestId 变为 2,发送请求 2(后发送,可能先响应)。
- 若请求 2 先响应:此时 currentId=2 与 requestId=2 相等,正常处理数据。
- 若请求 1 后响应:此时 currentId=1 与 requestId=2 不相等,直接忽略,避免旧数据覆盖新数据。
通过这种方式,无论请求响应顺序如何,最终只处理最新一次请求的结果,解决了 “慢请求覆盖快请求” 的竞态问题。

被折叠的 条评论
为什么被折叠?



