代表性面试主题

如何用 Web Locks API 协调多个标签页的共享任务?

前端困难
OfferCC 编辑团队发布 更新

题干

一个 Web 应用可能同时打开多个标签页,但只能有一个标签页刷新共享缓存并执行同步任务。请用 Web Locks API 设计协调方案,并说明锁的作用域、排队、ifAvailable、AbortSignal、标签页崩溃和不支持时的降级。

1. 题目

多个同源标签页会同时尝试刷新 IndexedDB 中的缓存并向服务端同步。请设计一个协调器,保证同一时刻最多一个上下文执行同步,其他标签页可以等待、跳过或在锁释放后重新检查状态。方案还要覆盖标签页关闭、网络失败和浏览器不支持 Web Locks 的情况。

2. 约束与澄清

  • 锁名在同源上下文之间共享,页面和 Worker 都可能参与竞争。
  • 锁只保护协调范围内的异步回调;它不是数据库事务,也不能替代服务端并发控制。
  • 同步任务可能耗时,需要可取消、可重试,并在完成后广播结果或版本号。
  • 需要区分等待锁、立即探测锁和放弃请求三种调用意图。

3. 核心思路

调用 navigator.locks.request(name, options, callback) 请求命名锁。锁只有在回调 Promise 完成后才释放;因此回调必须包含完整的读取、同步和状态写入流程。默认请求会排队,ifAvailable: true 可在锁忙时立即收到 null,signal 可取消仍在等待的请求。

锁管理器会在持有上下文终止时释放锁,但持锁页面崩溃不应成为业务正确性的唯一依据:同步记录应带版本、租期或幂等键,服务端仍需校验。其他标签页可通过 BroadcastChannel 或重新读取 IndexedDB 发现新版本。

4. 参考实现

javascript
const LOCK = "shared-cache-sync";

async function runSync(signal) {
  return navigator.locks.request(LOCK, { signal }, async (lock) => {
    if (!lock) return { status: "busy" };
    const current = await readSyncVersion();
    if (await isFresh(current)) return { status: "fresh" };
    const result = await fetchAndWriteCache({ signal, baseVersion: current });
    await publishVersion(result.version);
    return { status: "updated", version: result.version };
  });
}

async function trySyncWithoutWaiting() {
  return navigator.locks.request(
    LOCK,
    { ifAvailable: true },
    (lock) => lock ? runOneSync() : { status: "busy" },
  );
}

5. 正确性与故障处理

锁保证的是同源上下文对同一名称的互斥排队,不保证网络请求或数据库写入自动回滚。回调应先重新读取版本,再执行条件更新;如果同步期间网络失败,抛出错误让锁释放并由下一次请求重试。不要在锁回调之外保存“我拥有锁”的布尔值。

等待请求被 AbortSignal 取消时,调用方要区分取消与业务失败。对于长时间等待,可显示“其他标签页正在同步”或直接跳过;锁释放后再读取版本,避免重复刷新。同步结果通过 BroadcastChannel 通知只是优化,权威状态仍应来自 IndexedDB 或服务端。

6. 追问与陷阱

  • Web Locks 只在同源上下文间协调,不能锁住另一站点、服务器进程或数据库行。
  • ifAvailable 不会排队;拿不到锁时回调收到 null,不能把它当成异常。
  • steal: true 会破坏已有排队语义,应只用于明确可丢弃的工作,并配合版本检查。
  • 不支持 API 时可用 BroadcastChannel 加 IndexedDB 记录做尽力而为协调,但不能宣称具有同等互斥保证;服务端必须继续防重复。

7. 延伸阅读

可以比较 Web Locks、IndexedDB 事务、Service Worker 消息和 BroadcastChannel:锁解决跨上下文互斥,事务解决单库原子性,消息通道负责通知。跨设备或跨用户的互斥应移到服务端租约、幂等 API 或数据库约束。

8. 面试评分点

能说明锁的作用域

应指出命名锁只协调同源窗口与 Worker,回调 Promise 完成才释放,不能替代服务端或数据库并发控制。

能区分三种请求模式

应分别解释默认排队、ifAvailable 立即探测和 AbortSignal 放弃等待的行为。

能处理崩溃与重复

应使用版本、幂等键和条件更新,说明页面崩溃释放锁不等于业务写入自动回滚。

能给出诚实降级

应提供 BroadcastChannel/IndexedDB 的尽力而为方案,并明确互斥保证降低、服务端仍需兜底。

公开来源

同类题目

资源

产品对比

每份对比均基于官方资料,并标注复核日期。

查看完整对比