接口竞态问题

接口竞态问题 问题场景vue项目tab页面来回切换发送的是同一个请求当从页面a切换页面b,两个页面发送的同一个请求只是参数不同但是由于时间延迟导致覆盖上一个请求的数据这个情况下怎么解决这是典型的接口竞态问题A 页请求还没返回你切到 B 页发了新请求但 A 页的旧请求后返回把 B 页的数据覆盖了。常见解决方案有 3 种推荐优先用取消旧请求或请求标识校验。方案一切换 tab 时取消上一个请求推荐如果你用的是axios可以使用AbortController。Vue 示例let controller null async function getList(params) { // 取消上一次未完成的请求 if (controller) { controller.abort() } controller new AbortController() try { const res await axios.get(/api/list, { params, signal: controller.signal }) // 只有未被取消的请求才会走到这里 list.value res.data } catch (err) { if (err.name CanceledError || err.name AbortError) { console.log(请求被取消) return } console.error(err) } }tab 切换时function handleTabChange(tab) { getList({ type: tab }) }这样当你从 A 切到 B 时A 的请求会被取消不会再覆盖 B 的数据。方案二给每次请求加唯一标识只处理最后一次请求如果不想取消请求也可以让旧请求返回后不更新数据。let requestId 0 async function getList(params) { const currentId requestId const res await axios.get(/api/list, { params }) // 如果不是最后一次请求直接丢弃结果 if (currentId ! requestId) return list.value res.data }例如A 请求发出requestId 1B 请求发出requestId 2A 后返回发现1 ! 2不更新数据B 返回发现2 2更新数据这个方案非常适合 tab 快速切换场景。方案三不同 tab 使用独立数据源如果 A、B 页面虽然请求地址一样但参数不同数据也应该互不影响可以按 tab 缓存数据。const dataMap reactive({ a: [], b: [] }) async function getList(tab) { const res await axios.get(/api/list, { params: { type: tab } }) dataMap[tab] res.data }页面展示时const currentList computed(() dataMap[activeTab.value])这样 A 页的数据只放在dataMap.aB 页的数据只放在dataMap.b不会互相覆盖。推荐做法如果你的需求是页面只展示当前 tab 的数据推荐请求标识校验 loading 状态示例const list ref([]) const loading ref(false) let requestId 0 async function getList(params) { const currentId requestId loading.value true try { const res await axios.get(/api/list, { params }) if (currentId ! requestId) return list.value res.data } finally { if (currentId requestId) { loading.value false } } }如果你的需求是每个 tab 都保留自己的数据推荐每个 tab 独立缓存 请求标识校验Vue 组件卸载时也建议取消请求如果组件销毁了请求还没回来也可能出现异常更新。import { onBeforeUnmount } from vue let controller null onBeforeUnmount(() { if (controller) { controller.abort() } })总结这个问题本质是后发请求不一定后返回旧请求返回后覆盖了新数据。解决方式取消旧请求最干净推荐 axios AbortController请求 ID 校验只允许最后一次请求更新页面简单实用tab 独立缓存适合每个 tab 都要保留数据的场景实际项目中最常用的是let requestId 0 async function fetchData(params) { const id requestId const res await api(params) if (id ! requestId) return data.value res.data }这就可以避免 tab 快速切换时旧请求覆盖新数据的问题。