标准答案

  1. 竞态场景通常发生在搜索、筛选、分页、路由切换和快速重复提交中,旧请求可能比新请求更晚返回。
  2. 可以用 AbortController 取消旧请求,也可以用 requestId 或 token 判断返回结果是否仍然有效。
  3. watch 发起请求时,要利用 cleanup 在依赖变化前取消上一次请求或标记上一次结果失效。
  4. 组件卸载时要停止定时器、事件监听、订阅和未完成请求,避免更新已卸载组件状态。
  5. 请求层和页面层要分工清楚:请求层处理取消能力和错误规范,页面层处理当前业务是否仍需要这次结果。

题目解析

这道题的关键是“旧结果不能覆盖新状态”。只写 loading true/false 很容易在并发请求下出错。

AbortController 更适合支持取消的 fetch 请求;token 判断适合接口库不支持取消或只需要忽略旧结果的场景。

组件卸载后的状态更新在 Vue 里不一定总是直接报错,但仍然会造成无意义逻辑、错误提示错位或内存泄漏。

代码示例

下面用 AbortController 和 watch cleanup 处理搜索请求竞态。

TypeScript
watch(
  keyword,
  async (value, _oldValue, onCleanup) => {
    const controller = new AbortController()
    onCleanup(() => controller.abort())

    loading.value = true

    try {
      result.value = await fetchSearch(value, {
        signal: controller.signal
      })
    } finally {
      if (!controller.signal.aborted) {
        loading.value = false
      }
    }
  }
)

常见误区

  • 快速切换筛选条件时没有处理旧请求,导致页面显示过期数据。
  • 组件卸载后请求仍然回写状态,造成提示错位或无意义更新。
  • 只取消请求,没有处理 loading、error 和空态在取消后的表现。

作者信息