Web Locks API通过navigator.locks.request()提供原生并发控制,解决跨上下文数据冲突问题。它支持exclusive(独占)和shared(共享)两种模式,分别用于写操作和读操作的协调,实现“多读单写”的高效同步。开发者可利用锁名称统一标识资源,结合options配置ifAvailable、steal和AbortSignal等行为,避免竞态条件与资源争用。相比localStorage事件或postMessage等手动方案,Web Locks具备原子性、自动释放、浏览器级可靠性等优势。典型应用场景包括全局状态更新、缓存读写、资源预加载等。使用时需警惕死锁、长时间持锁、错误处理缺失等问题,最佳实践包括缩小锁粒度、统一命名、避免嵌套锁、结合AbortSignal取消请求,并利用navigator.locks.query()进行调试监控,确保并发安全与系统稳定性。

在现代Web应用中,尤其当你的应用需要在多个浏览器标签页、Web Worker甚至Service Worker之间协调工作时,管理共享资源和并发访问无疑是个老大难问题。简单来说,Web Locks API提供了一种原生的、标准化的方式,让你能够安全地锁定一个共享资源,确保在某个时刻只有一个(或特定数量的)代码段能够访问它,从而有效避免数据冲突和竞态条件。这就像给你的数据加了一把锁,只有拿到钥匙的人才能进去操作,大大简化了跨上下文同步的复杂性。
使用Web Locks API的核心在于
navigator.locks.request()
基本的用法是这样的:
async function updateSharedCounter() {
const lockName = 'my_shared_counter_lock';
try {
await navigator.locks.request(lockName, async lock => {
// 成功获取到锁,现在可以安全地访问共享资源了
console.log(`Lock '${lockName}' acquired.`);
// 模拟一个耗时操作,例如从localStorage读取、修改、写入
let count = parseInt(localStorage.getItem('shared_counter') || '0', 10);
count++;
localStorage.setItem('shared_counter', count.toString());
console.log(`Counter updated to: ${count}`);
// 锁会在这个异步操作完成后自动释放
// 如果这里有其他异步操作,确保它们在锁的范围内完成
await new Promise(resolve => setTimeout(resolve, 500)); // 模拟异步工作
});
console.log(`Lock '${lockName}' released.`);
} catch (error) {
// 请求锁失败或回调函数中出现错误
console.error(`Failed to acquire or execute with lock '${lockName}':`, error);
}
}
// 可以在多个标签页或worker中同时调用
updateSharedCounter();navigator.locks.request()
name
callback
options
exclusive
shared
ifAvailable
steal
AbortSignal
模式选择上,
exclusive
shared
exclusive
shared
// 带有选项的请求示例
async function tryUpdateCounterConditionally() {
const lockName = 'my_shared_counter_lock';
const options = {
mode: 'exclusive',
ifAvailable: true // 如果锁不可用,则立即拒绝,而不是等待
};
try {
const lock = await navigator.locks.request(lockName, options, async lock => {
console.log(`Conditionally acquired lock '${lockName}'.`);
// ... 执行操作 ...
});
if (!lock) {
console.log(`Lock '${lockName}' was not available, skipping update.`);
}
} catch (error) {
console.error(`Error during conditional lock request:`, error);
}
}值得一提的是,你还可以通过
navigator.locks.query()
在我看来,Web Locks API的出现,简直是对Web并发编程领域的一次“降维打击”。过去,我们为了在不同浏览器标签页或Worker之间同步状态,简直是绞尽脑汁,各种土法炼钢。你可能尝试过利用
localStorage
IndexedDB
postMessage
想想看,手动管理一个全局的“忙碌”状态,需要确保所有操作都正确地设置和清除了这个状态,一旦有任何遗漏,数据不一致就可能发生。而且,这些自定义方案往往缺乏原子性,一个复杂的操作可能被中断,留下一个半成品的数据状态。Web Locks API则不然,它是由浏览器底层提供的原生能力,天生就具备原子性、可靠性和跨上下文的可见性。你不需要再担心某个标签页崩溃导致锁永远不释放(浏览器会自动处理),也不用编写大量的样板代码来协调消息。它把并发控制的复杂性封装起来,暴露一个简洁的API,让我们能更专注于业务逻辑本身,而不是与底层机制搏斗。这就像从自己造轮子,直接升级到了使用汽车厂商提供的标准组件,效率和稳定性都不可同日而语。
exclusive
shared
exclusive
shared
exclusive
localStorage
IndexedDB
exclusive
// 示例:独占更新用户偏好设置
async function updateUserSettings(newSettings) {
await navigator.locks.request('user_settings_lock', async () => {
const currentSettings = JSON.parse(localStorage.getItem('userSettings') || '{}');
const mergedSettings = { ...currentSettings, ...newSettings };
localStorage.setItem('userSettings', JSON.stringify(mergedSettings));
console.log('User settings updated exclusively.');
});
}IndexedDB
exclusive
shared
缓存读取与更新: 这是
shared
IndexedDB
exclusive
// 示例:共享读取缓存,独占更新缓存
async function readFromCache(key) {
let data = null;
await navigator.locks.request('data_cache_lock', { mode: 'shared' }, async () => {
data = localStorage.getItem(`cache_${key}`);
console.log(`Read from cache (shared): ${key}`);
});
return data;
}
async function updateCache(key, value) {
await navigator.locks.request('data_cache_lock', { mode: 'exclusive' }, async () => {
localStorage.setItem(`cache_${key}`, value);
console.log(`Updated cache (exclusive): ${key}`);
});
}
// 多个标签页可以同时调用 readFromCache
// 只有一个标签页可以调用 updateCache,当 updateCache 运行时,所有 readFromCache 都会被阻塞资源预加载状态管理: 多个页面可能都需要同一个大型资源(如一个WebAssembly模块或大型图片)。
shared
exclusive
日志记录: 多个上下文可以同时向一个共享的日志存储写入日志,但如果需要执行一个日志归档或清理的特殊操作,则可能需要一个
exclusive
这种读写分离的模式,在我看来,极大地提高了并发系统的效率,因为它允许并发读取,只在真正需要修改数据时才引入独占阻塞,这在Web应用中非常常见。
Web Locks API虽然强大,但并非万能药,使用不当同样会引入新的问题。在我有限的经验中,以下几点是需要特别注意的:
常见陷阱:
ifAvailable
steal
ifAvailable: true
request
null
steal: true
navigator.locks.request()
AbortSignal
try...catch
最佳实践:
保持锁的粒度尽可能小,持有时间尽可能短: 只在真正需要保护共享资源的代码块中使用锁,并且一旦操作完成,就让锁自动释放。避免在锁的回调函数中执行与共享资源无关的、耗时的操作。
统一命名约定: 为你的锁选择清晰、一致的名称。所有需要协调访问某个资源的上下文,都必须使用完全相同的锁名称。一个好的做法是使用模块名或资源路径作为锁名称的一部分。
避免嵌套锁,或确保一致的锁请求顺序: 如果你确实需要获取多个锁,请确保在所有上下文中都以相同的顺序请求它们,这是避免死锁的关键策略。
利用AbortSignal
AbortSignal
request
AbortSignal
const controller = new AbortController();
const signal = controller.signal;
// 某个事件触发时,取消锁请求
// controller.abort();
navigator.locks.request('cancellable_task_lock', { signal }, async () => {
// ... 任务逻辑 ...
}).catch(error => {
if (error.name === 'AbortError') {
console.log('Lock request was aborted.');
} else {
console.error('Lock request failed:', error);
}
});结合Web Workers: 即使使用了Web Locks API,长时间运行或计算密集型任务仍然应该在Web Worker中执行,以避免阻塞主线程,确保用户界面的响应性。锁可以用于协调Worker与主线程之间,或不同Worker之间的共享数据访问。
监控与调试: 使用
navigator.locks.query()
总而言之,Web Locks API是一个强大的工具,它极大地简化了Web平台上的并发控制。但就像任何强大的工具一样,理解其工作原理、潜在风险并遵循最佳实践,才能真正发挥它的价值,构建出健壮、高效的Web应用。
以上就是如何用Web Locks API管理资源共享与并发访问?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号