web locks api 无法解决 localstorage 并发写入冲突,因其异步锁无法干预同步阻塞式 setitem;应改用 indexeddb + web locks 组合、服务端幂等控制或 storage 事件 fallback 方案。

Web Locks API 不能“异步解决”原始数据写入冲突,它本身不操作数据、不修改存储行为,只是提供跨标签页的互斥协调能力。真正起作用的是你如何用它包裹读-改-写这一整段逻辑,并确保底层存储支持事务与异步完成确认。
关键在于:锁不是加在“写”这一步上,而是加在整个有状态变更意图的业务流程上——尤其当多个页面可能基于同一旧值做计算再写回时(如计数器+1、购物车增删、笔记编辑保存)。
锁必须配合支持事务的存储方案
localStorage 完全不适用。它的 setItem 是同步阻塞调用,浏览器立即落盘,Web Locks 的 Promise 根本插不进执行流。加锁后两个标签页仍会先后执行 setItem,覆盖不可避免。
✅ 正确路径是:IndexedDB + Web Locks
- IndexedDB 提供单次事务的原子性(
transaction('readwrite')) - Web Locks 提供跨页面的串行化准入控制(谁先拿到锁,谁先走完整流程)
例如更新用户主题设置:
async function updateTheme(theme) {
await navigator.locks.request('user-theme-write', async () => {
const db = await openDB('myapp', 1);
const tx = db.transaction('settings', 'readwrite');
const store = tx.objectStore('settings');
// 先读出当前值(避免基于过期快照计算)
const current = await store.get('theme') || { value: 'light' };
// 执行业务逻辑(轻量!别放耗时计算)
const newValue = { key: 'theme', value: theme, updatedAt: Date.now() };
// 写入
await store.put(newValue, 'theme');
// ⚠️ 必须等待事务真正完成,否则锁提前释放会导致竞态
await tx.done;
});
}
锁名设计决定是否真能防冲突
- 错误:用
'db-write'这类全局锁 → 所有写操作排队,性能差,且无关数据互相阻塞 - 正确:按业务实体粒度命名,如
'user-theme-write'、'cart-item-789'、'note-123-edit' - 锁名中带用户 ID 或资源 ID,才能保证不同用户的操作不互相干扰
必须显式处理锁不可用的情况
不能默认“锁一定拿得到”。高交互场景建议加超时或降级:
const signal = AbortSignal.timeout(300); // 300ms 后自动放弃
try {
await navigator.locks.request('cart-update', { ifAvailable: true }, async () => {
// 执行写逻辑
});
} catch (err) {
if (err.name === 'AbortError') {
// 提示用户:“正在被其他窗口编辑,请稍后再试”
}
}
Safari 等不支持环境必须有 fallback
Safari 目前(iOS 17.5 / macOS 14.5)完全不支持 Web Locks。不能跳过逻辑,否则多标签页直接裸奔。
可行 fallback 方案包括:
- 前端用
localStorage存临时标记(如lock:cart:${userId}+ 时间戳),配合storage事件监听并弹窗提醒 - 后端接口强制幂等:前端生成唯一
requestId,服务端拒绝重复 ID 的写请求 - 对非关键数据,接受最终一致,用 readonly 事务读取 + 乐观校验(如比对
updatedAt字段)
不复杂但容易忽略。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










