如何用 Web Locks API 协调多个 Service Worker 实例对本地索引数据库的并发写入操作 开门见山,先说一个核心结论:Web Locks API 并不能用来协调多个 Service Worker 实例之间的锁。原因很简单:在同一源下,浏览器只允许一个 Service Worke

开门见山,先说一个核心结论:Web Locks API 并不能用来协调多个 Service Worker 实例之间的锁。原因很简单:在同一源下,浏览器只允许一个 Service Worker 处于激活状态。所谓的“多个实例并发运行”,其实是一个常见的误解。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
要理解这一点,得从 Service Worker 的生命周期说起。它的状态流转是严格受控的:install → waiting → active。新版本安装后,旧版本虽然可能还在运行(比如服务于已打开的页面),但它已经进入了“退休”倒计时。一旦所有关联的客户端关闭或跳转,旧版本就会被终止,新版本随即上位成为唯一的 active 实例。
这意味着什么?意味着你永远无法在运行时同时拥有两个处于 active 状态的 Service Worker。所以,我们平时讨论的“多个实例”,实质上是不同版本在切换过程中的短暂共存,而非真正意义上的并行执行。
na vigator.locks 在 Service Worker 中确实可用,但锁的作用域是“同源 + 同一锁管理器”。关键在于,这个锁管理器对于每个 Service Worker 的生命周期是独占的。skipWaiting() 强制升级,也只是完成了一次替换,而不是在原有基础上叠加了一个新的运行实例。既然问题不在多个 Service Worker 之间,那么并发冲突究竟发生在哪里?答案通常藏在以下几个组合里:
主线程 和 激活的 Service Worker 同时调用 indexedDB.open() 并执行写事务。Web Worker(注意,不是 Service Worker)与主线程或 Service Worker 共享同一个 IndexedDB 数据库。标签页 中的主线程,都试图写入同一个数据库,尤其是在使用 versionchange 事务进行数据库结构升级时。在这些场景下,IndexedDB 自身的事务隔离机制(比如 readonly、readwrite、versionchange)提供了基础的保障。但是,它无法防止逻辑层的重复写入或竞态更新。举个典型的例子:“读-改-写”这个操作序列,如果没有锁的保护,就可能出现数据错乱。这时,才是 Web Locks API 真正该登场的时候。
关键思路要转变:锁的目标不是“Service Worker”这个执行环境,而是具体的“资源”。通常,我们会用一个精心设计的锁名来标识资源,比如“数据库名 + 表名 + 主键”,从而确保对同一资源的操作是串行化的。
async function writeUserRecord(userId, data) {
// 锁粒度建议:按业务实体,而非整个数据库。过粗的锁会严重影响性能。
const lockName = `idb:user:${userId}`;
await na vigator.locks.request(lockName, { mode: 'exclusive' }, async (lock) => {
const db = await openDB(); // 这里假设是封装好的 indexedDB.open()
const tx = db.transaction('users', 'readwrite');
const store = tx.objectStore('users');
// 注意:所有写操作必须在锁的持有期间内完成
await store.put({ id: userId, ...data });
await tx.done; // 等待事务提交完成,这是关键一步
});
}
postMessage),这会导致锁的释放时机变得不可预测。tx.done 是一种 Promise 化的封装(也可以用 new Promise(r => tx.oncomplete = r) 替代),它的作用是确保数据真正写入磁盘后再释放锁。active 状态,否则 na vigator.locks 可能尚未就绪。技术选型不能只看理想情况,Web Locks API 在部分环境下的表现需要特别注意:
mode: 'upgrade' 模式。此外,在 Service Worker 脚本中,query() 方法目前并不可用。localStorage 做标记配合轮询,或者干脆依赖 IndexedDB 事务的重试机制。话说回来,真正棘手的问题从来不是“怎么加锁”,而是“锁什么”以及“什么时候释放最安全”。特别是当一次写操作不仅涉及数据库,还可能触发缓存更新(caches.put)、推送通知,甚至需要同步到其他标签页时,锁的范围必须覆盖所有这些副作用。否则,数据的一致性链条就可能断裂,留下难以排查的隐患。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述