关键时刻可以救命的Web Locks API
内容提要
多标签页同时上传同一文件会导致进度混乱,localStorage轮询和Broadcast Channel等跨页通信方案存在竞态或冲突。Web Locks API通过navigator.locks.request()加锁,确保仅一个标签页上传,其他静默跳过,从而避免重复上传、提升体验并简化代码。
延伸解读
多标签页上传冲突的根源
当用户同时打开多个相同URL的断点续传页面时,每个标签页都会独立向后端发送上传数据,导致进度反馈混乱。文章指出,这种冲突源于缺乏跨标签页的协调机制,而常见的localStorage轮询和Broadcast Channel方案存在竞态条件或消息冲突,无法可靠地保证同一时间只有一个上传任务。
Web Locks API 的解决思路
Web Locks API通过navigator.locks.request()为共享资源加锁,确保同一时间只有一个标签页能获得锁并执行上传。其他标签页在请求锁时若发现已被占用,则静默跳过,从而避免重复上传。锁的生命周期由回调函数管理,上传完成后自动释放,简化了协调逻辑。
关键参数与使用注意
navigator.locks.request()支持多个可选参数:mode可指定排他锁或共享锁;ifAvailable为true时,若锁已被占用则回调收到null而非等待;steal可强制释放同名锁,但需谨慎使用,因为原持有锁的代码可能继续运行并引发冲突;signal可传入AbortSignal以取消锁请求。这些参数在多数场景下并非必需。
适用场景与兼容性
除了大文件断点续传,Web Locks API还适用于多标签页同时操作IndexedDB或localStorage、文档编辑器多标签页编辑同一文档、单页面内按顺序执行复杂异步任务等场景。该API已出现多年,兼容性良好,且navigator.locks.query()方法可查询已持有和挂起的锁信息。
Q&A
多个标签页同时上传同一个文件会导致什么问题?
会导致进度混乱,每个页面的上传进度不一样,后端返回的进度一会儿30%,一会儿25%,一会儿又是35%,进度条疯狂跳动,用户体验极差。
为什么localStorage加轮询不能解决多标签页上传冲突?
因为localStorage的读/写操作不是原子操作,存在竞态条件;如果标签页崩溃,锁定状态会永久保留;需要持续轮询检查锁定状态,开销大;存储事件只会在其他标签页触发,不会在当前标签页触发。
Broadcast Channel API用于多标签页上传协调有什么问题?
同时发送的消息可能会造成冲突,需要手动处理心跳检测、崩溃检测和冲突解决,无法真正解决共享文件上传问题。
Web Locks API如何解决多标签页上传冲突?
通过navigator.locks.request()方法加锁,只有成功获取锁的标签页才执行上传,其他标签页获取不到锁则静默跳过,从而确保仅一个标签页上传,避免重复上传。
navigator.locks.request()方法有哪些可选参数?
可选参数包括:mode('exclusive'或'shared',默认'exclusive')、ifAvailable(为true时只有未持有锁才授予,否则回调收到null)、steal(为true时释放所有同名已持有的锁并授予该请求,需小心使用)、signal(AbortSignal,中止时丢弃未授予的锁请求)。
除了大文件断点续传,Web Locks API还适合哪些场景?
适合多个标签页同时操作Indexed Database API或LocalStorage时;网页端文档编辑器中用户在两个标签页打开同一份文档时;单页面内按顺序执行复杂异步任务(如批量文件上传、连续串联请求)时。