异步序列中处理状态竞争的核心是设计明确的修改权限、时机与验证机制:用AbortController取消过期请求、requestId校验响应时效、内存锁/版本号控制并发写入、标志位串行化简单操作。

异步序列中处理状态竞争,核心是让多个并发操作对同一状态的修改变得可预测、不冲突。这不是靠“等它做完”,而是靠设计机制来明确谁有资格改、什么时候改、改完怎么验证。
用 AbortController 主动取消过期请求
适合搜索建议、输入联想、刷新重试等场景。关键不是发得快,而是停得准:
- 每次发起新请求前,调用上一个
AbortController.abort() - 把
controller.signal传给fetch或支持 signal 的 API - 在
catch中检查err.name === 'AbortError',遇到就静默丢弃,不更新 UI 或状态 - 每个请求配独立控制器,不复用、不跨组件共享
用 requestId 或时间戳做响应时效校验
当请求无法取消(如封装好的 Promise、WebSocket 消息),就靠“拒收”来防御:
- 发起请求时生成唯一标识,比如
Date.now()或crypto.randomUUID() - 把这个 id 存为当前最新
latestRequestId,并随请求一起发出(可放 header、query 或 body) - 响应返回后,先比对它携带的
responseId是否等于latestRequestId - 不相等 → 说明已有更新请求发出,直接忽略该响应,不执行
setState或数据合并
用内存锁或版本号控制并发写入
针对草稿保存、计数器更新、多标签页编辑等共享状态场景:
- 客户端可用
Map记录 pending 的 key,例如pendingWrites.has('draft-123') - 修改前先
pendingWrites.set('draft-123', true),完成后delete - 服务端配合乐观锁:读取时带
version字段,更新时加WHERE version = ?,失败则重试或提示冲突 - 多窗口同步可用
BroadcastChannel+ 版本号:本地升版、广播、其他窗口只在收到更高版本时才拉取更新
简单串行化:标志位就够了
如果只是防重复提交、自动保存这类无需保留中间结果的操作:
- 声明
let isSaving = false - 进入异步函数前加判断:
if (isSaving) return - 设置
isSaving = true,请求结束后在finally块里设回false - 注意:它把并发变排队,不适合需要响应实时性但又不能错乱的复杂交互
不复杂但容易忽略。


















