前言
前端里常见的问题请求竞态。 尤其是搜索、筛选、联动查询这类高频场景,用户操作很快,请求也会连续发出。 但接口返回顺序不一定和发出顺序一致,最后就很容易出现:
新请求发出去了,旧请求却最后返回 页面被旧数据覆盖 UI 状态和用户操作对不上
问题复现
比如搜索框里做联想查询:
- 用户输入 a,发起一次请求
- 很快又输入 ab,再发一次请求
- 接着输入 abc,继续发请求
正常情况下,最后页面应该展示 abc 对应的数据。 但如果 a 这个请求返回得更晚,就可能把 abc 的结果覆盖掉。
结果就是:
用户明明输入的是 abc 页面却显示 a 的结果 体验非常差 这个问题的根源,就是异步请求返回顺序不可控。
实现思路 思路其实很简单:
每次调用时,给请求一个递增序号 记录当前最新的序号 请求返回时,检查自己是不是最新那次 如果不是,直接丢弃结果 这样就能保证:
只有最后一次调用的结果会生效,之前的请求全部失效。
完整代码:
export type AnyAsyncFunction = (...args: any[]) => Promise<any>;
/**
* 高阶函数:解决频繁请求的竞态问题
*/
export function createLatestRequest<T extends AnyAsyncFunction>(fn: T): T {
let lastSeq = 0;
return ((...args: Parameters<T>) => {
const currentSeq = ++lastSeq;
return fn(...args).then(
(result) => {
if (currentSeq !== lastSeq) {
return new Promise<never>(() => {});
}
return result;
},
(error) => {
if (currentSeq !== lastSeq) {
return new Promise<never>(() => {});
}
throw error;
},
);
}) as T;
}
1. 类型定义