背景
搜索页连续输入时,旧请求可能比新请求更晚返回。单独维护 loading、error、data,既容易组合出矛盾状态,也掩盖了响应乱序。
核心判断
- 用 idle、loading、success、error 表达互斥状态,空列表属于 success。
- 每次请求携带递增序号或取消信号,只有当前请求可以提交结果。
- 刷新旧数据时另行定义 refreshing 状态,明确是否保留旧内容。
最小示例
下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。
type State<T> =
| { kind: "idle" }
| { kind: "loading" }
| { kind: "success"; data: T }
| { kind: "error"; message: string };
const state = shallowRef<State<User[]>>({ kind: "idle" });
let sequence = 0;
async function search(query: string) {
const current = ++sequence;
state.value = { kind: "loading" };
try {
const data = await api.search(query);
if (current === sequence) state.value = { kind: "success", data };
} catch (error) {
if (current === sequence) {
state.value = { kind: "error", message: String(error) };
}
}
}
边界与误区
状态类型只能约束组合,不能自动解决竞态。仅在 finally 里设置 loading=false,也可能让旧请求提前结束新请求的加载提示。
验证方式
用可控 Promise 让第二次请求先完成,确认第一次响应被丢弃;再覆盖初始、空结果、失败、取消和重试的界面表现。
小结
用有限状态表达 idle、loading、success、error,避免多个布尔值形成不可能组合。