navigator.locks 无法协调多窗口对 localstorage 的并发写入,因其同步阻塞特性使锁无法介入;需改用 indexeddb 等支持事务的异步存储方案,或采用后端幂等+乐观更新策略。

navigator.locks 不能协调多窗口对 localStorage 的并发写入——这不是 API 用错了,而是根本不在它能管的范围内。
localStorage.setItem 是同步阻塞操作,锁根本插不进去
-
localStorage所有方法(setItem、getItem、removeItem)都是立即执行、无回调、不返回 Promise 的同步调用 -
navigator.locks.request()只能包裹异步逻辑(比如fetch、indexedDB),它的回调函数本身是异步入口,但一旦你往里面写localStorage.setItem('x', 'y'),这行代码就立刻落盘,锁对此毫无感知 - 多个标签页几乎同时拿到锁、同时执行
setItem,结果还是覆盖写,不是串行写
localStorage 没有事务、没有版本号、不支持原子读-改-写,浏览器也不提供任何跨 tab 同步写入保障机制。
真正需要协调时,必须换掉 localStorage
如果你的场景确实要求「多窗口协作更新同一份状态」(比如实时计数器、协同草稿、用户偏好快照),只能迁移到支持事务和异步的存储方案:
- 使用
indexedDB存储结构化数据,它天然支持transaction('store', 'readwrite')保证单次写入原子性 - 把「读当前值 → 计算新值 → 写回」整个流程包进
navigator.locks.request('my-state', async lock => { ... }) - 锁名必须带业务上下文,例如:
update-preferences-${userId},避免所有用户抢同一个锁
示例片段:
navigator.locks.request('user-theme', async lock => {
const db = await openIDB();
const tx = db.transaction('settings', 'readwrite');
const store = tx.objectStore('settings');
const current = await store.get('theme');
await store.put({ key: 'theme', value: current?.value === 'dark' ? 'light' : 'dark' });
await tx.done; // 等待事务真正完成再释放锁
});
如果非要留在 localStorage,只能做弱协调提示
- 写入前用
localStorage.setItem('lock:cart', Date.now() + '')标记“我正在编辑” - 监听
storage事件,发现别人也写了同名 lock,就弹个提示:“其他窗口正在修改购物车,请刷新确认” - 这不是锁,只是协作信号;无法阻止竞态,只能降低误操作概率
真正难的从来不是调用 navigator.locks.request,而是判断:这个状态到底该不该由前端多窗口强一致维护?很多情况下,后端幂等接口 + 客户端乐观更新,比死磕前端锁更可靠。










