??你要在watch中执行异步方法,那你就得明白什么是竟态问题!

请问一下,??你要在watch中执行异步方法,那你就得明白什么是竟态问题!
最新回答
初心不负

2026-03-04 10:22:02

竞态问题(Race Condition) 是指在系统或进程中,输出结果依赖于不受控制的事件出现顺序或时机,尤其在异步操作中,多个并发请求的响应顺序错乱会导致非预期结果。

竞态问题的典型场景

以输入框搜索为例:

  • 用户在输入框中连续输入字母时,每次输入都会触发一个异步请求获取搜索结果。
  • 若网络延迟导致先发送的请求后返回,后发送的请求先返回,则最终显示的搜索结果可能与用户当前输入的关键词不匹配。

竞态问题的解决方案1. 中断请求

在下一个请求开始前取消上一个未完成的请求,确保只有最新请求的响应被处理。

  • 实现原理:通过保存上一次请求的取消方法(如 AbortController.abort()),在新请求触发时调用该方法中断旧请求。
  • 示例代码:export const doAsyncWork = (newId) => { const controller = new AbortController(); const response = axios.get('/user', { params: { id: newId }, signal: controller.signal }); return { cancel: () => controller.abort(), response };};
2. 忽略过期请求结果

通过标记请求的过期状态,仅处理最新请求的响应,丢弃过期的结果。

  • 实现原理:利用闭包保存请求的 expired 状态,在下一次请求触发时标记上一次请求为过期。
  • 示例代码:watch(keyword, async (newValue, oldValue, onCleanup) => { let expired = false; onCleanup(() => { expired = true; }); const res = await getSeachResult(newValue); if (!expired) result.value = res;}, { immediate: true });
watch 中 onCleanup 的作用与原理

onCleanup 是 watch 回调函数的第三个参数,用于注册副作用清理函数,其核心作用是解决竞态问题。

1. 中断请求的实现
  • 原理:

    watch 内部通过 cleanup 变量保存用户传入的取消方法(如 cancel)。

    每次监听回调触发时,先执行 cleanup 取消上一次请求,再更新 cleanup 为当前请求的取消方法。

  • 代码解析:function watch(source, cb, options = {}) { let cleanup; function onCleanup(fn) { cleanup = fn; } // 保存取消方法 const job = () => { if (cleanup) cleanup(); // 取消上一次请求 cb(newValue, oldValue, onCleanup); // 执行回调,传入当前onCleanup };}

2. 忽略过期请求的实现
  • 原理:

    通过闭包维护 expired 状态,标记上一次请求是否过期。

    在 onCleanup 中注册过期标记函数,下次回调触发时修改上一次的 expired 为 true。

  • 关键点:

    闭包确保每个请求的 expired 状态独立。

    仅当 expired 为 false 时更新结果,避免竞态。

总结
  • 竞态问题本质:异步操作中请求与响应的顺序错乱。
  • watch 的解决方案:

    中断请求:通过 onCleanup 保存并执行取消方法,确保只有最新请求生效。

    忽略结果:通过闭包标记请求状态,丢弃过期响应。

  • onCleanup 的角色:作为副作用清理的入口,统一管理竞态问题的解决逻辑。

通过合理使用 onCleanup,可以高效避免 watch 中异步操作引发的竞态问题,提升应用的稳定性和用户体验。