try-catch不能解决竞态条件,仅捕获异常而不干预时序;竞态源于共享可变状态的非原子访问,需靠锁、不可变数据或服务端幂等机制保障一致性。

在 async 函数中使用 try-catch 本身不能解决并发竞争(race condition)问题,它只负责捕获和处理错误,不干预执行时序或资源访问逻辑。并发竞争的稳定性需靠同步机制保障,try-catch 是兜底手段,不是预防手段。
并发竞争的本质不在异常,而在状态冲突
async/await 是语法糖,底层仍是 Promise 和事件循环。多个 await 操作若共享同一可变状态(如全局变量、缓存对象、DOM 节点、数据库记录),而未加锁或串行化,就可能因执行顺序不确定导致数据错乱——这属于逻辑错误,通常不会抛出 JS 异常,也就无法被 try-catch 捕获。
例如两个并发请求都读取 count = 5,各自加 1 后写回,最终结果可能是 6 而非预期的 7。这种竞态不会触发 error,但破坏业务一致性。
try-catch 能帮上的有限场景
- 捕获 Promise 拒绝引发的显式异常:如 fetch 超时、API 返回 500、JSON 解析失败等,这些会 reject Promise,await 触发 throw,可被 catch 捕获;
- 包裹关键临界区后的清理逻辑:在加锁(如使用 acquireLock() + releaseLock())后,用 finally 确保锁释放,避免死锁;
- 降级兜底,防止竞态引发的连锁崩溃:比如并发更新失败后,catch 中回退到本地缓存读取,维持页面可用性。
真正提升并发稳定性的做法
- 避免共享可变状态:优先使用不可变数据结构,或让每个异步操作持有独立副本;
- 显式协调执行顺序:用 Promise.allSettled 替代 all(不因单个失败中断全部),或用队列(如 p-queue 库)控制并发数;
- 服务端配合幂等与乐观锁:前端传 version 或 etag,后端校验并拒绝过期更新;
- 使用原子操作抽象:如 Zustand 的 setState({ ... }, { type: 'replace' }) 或 Redux Toolkit 的 createAsyncThunk 自动处理 pending/fulfilled/rejected 状态,把竞态隔离在状态管理层。
一个典型误用示例
以下代码看似“健壮”,实则无效:
async function updateProfile(name) {
try {
const res = await fetch('/api/profile', { method: 'PATCH', body: JSON.stringify({ name }) });
const data = await res.json();
user.name = data.name; // ❌ 多个并发调用会相互覆盖
} catch (e) {
console.error('更新失败');
}
}
catch 捕获了网络错误,但对两个 updateProfile("A") 和 updateProfile("B") 并发执行导致的 name 被后者覆盖,完全无能为力。


















