97色伦色在线综合视频,无玛专区,18videosex性欧美黑色,日韩黄色电影免费在线观看,国产精品伦理一区二区三区,在线视频欧美日韩,亚洲欧美在线中文字幕不卡

酒店加盟什么網(wǎng)站建設(shè)廣東建設(shè)廳官網(wǎng)

鶴壁市浩天電氣有限公司 2026/01/24 17:16:06
酒店加盟什么網(wǎng)站建設(shè),廣東建設(shè)廳官網(wǎng),網(wǎng)站的公告欄怎么做,做app找什么公司尊敬的各位技術(shù)同仁#xff0c;大家好#xff01;在現(xiàn)代復(fù)雜的前端應(yīng)用開發(fā)中#xff0c;我們經(jīng)常面臨一個(gè)挑戰(zhàn)#xff1a;如何在用戶同時(shí)打開的多個(gè)瀏覽器 Tab 頁(yè)之間#xff0c;保持?jǐn)?shù)據(jù)的強(qiáng)一致性。想象一下#xff0c;一個(gè)用戶在一個(gè) Tab 頁(yè)修改了某個(gè)設(shè)置#xff0…尊敬的各位技術(shù)同仁大家好在現(xiàn)代復(fù)雜的前端應(yīng)用開發(fā)中我們經(jīng)常面臨一個(gè)挑戰(zhàn)如何在用戶同時(shí)打開的多個(gè)瀏覽器 Tab 頁(yè)之間保持?jǐn)?shù)據(jù)的強(qiáng)一致性。想象一下一個(gè)用戶在一個(gè) Tab 頁(yè)修改了某個(gè)設(shè)置而另一個(gè) Tab 頁(yè)卻依然顯示著舊的數(shù)據(jù)或者多個(gè) Tab 頁(yè)同時(shí)嘗試更新同一個(gè)資源導(dǎo)致數(shù)據(jù)沖突或丟失。這些場(chǎng)景輕則影響用戶體驗(yàn)重則引發(fā)嚴(yán)重的業(yè)務(wù)邏輯錯(cuò)誤。今天我們將深入探討如何利用 Web 平臺(tái)提供的兩大強(qiáng)大工具——SharedWorker和Lock API——來(lái)構(gòu)建一個(gè)跨 Tab 頁(yè)的強(qiáng)一致性通信機(jī)制從而有效解決這些并發(fā)與同步問題。我們將從問題的根源出發(fā)逐步剖析這兩種技術(shù)的原理最終通過具體的代碼示例展示如何將它們巧妙結(jié)合實(shí)現(xiàn)我們所需的高可靠性系統(tǒng)。跨 Tab 頁(yè)通信的挑戰(zhàn)與強(qiáng)一致性需求瀏覽器天然的設(shè)計(jì)哲學(xué)是隔離。每個(gè) Tab 頁(yè)通常運(yùn)行在獨(dú)立的進(jìn)程或線程中擁有獨(dú)立的 JavaScript 運(yùn)行時(shí)、DOM 樹和內(nèi)存空間。這種隔離性保障了安全性與穩(wěn)定性但也為跨 Tab 頁(yè)的數(shù)據(jù)共享與同步帶來(lái)了挑戰(zhàn)。傳統(tǒng)跨 Tab 頁(yè)通信手段及其局限在深入探討解決方案之前我們先回顧一下常見的跨 Tab 頁(yè)通信手段并分析它們?cè)趯?shí)現(xiàn)“強(qiáng)一致性”方面的不足localStorage/sessionStorage:優(yōu)點(diǎn):簡(jiǎn)單易用數(shù)據(jù)持久化localStorage跨 Tab 頁(yè)共享。缺點(diǎn):非原子性:對(duì)localStorage的寫入操作不是原子的。如果兩個(gè) Tab 頁(yè)幾乎同時(shí)讀取、修改、寫入同一個(gè)鍵值很容易發(fā)生競(jìng)態(tài)條件導(dǎo)致后寫入的數(shù)據(jù)覆蓋前寫入的數(shù)據(jù)或者基于舊數(shù)據(jù)進(jìn)行的計(jì)算結(jié)果被覆蓋。無(wú)通知機(jī)制:localStorage的storage事件只能通知到非當(dāng)前寫入的 Tab 頁(yè)無(wú)法通知到當(dāng)前寫入的 Tab 頁(yè)。這使得同步邏輯變得復(fù)雜。容量限制:通常為 5-10MB。強(qiáng)一致性挑戰(zhàn):缺乏原生的鎖定機(jī)制無(wú)法保證對(duì)共享數(shù)據(jù)的并發(fā)訪問是安全的。BroadcastChannelAPI:優(yōu)點(diǎn):專門為跨 Tab 頁(yè)同源廣播消息設(shè)計(jì)API 簡(jiǎn)潔。缺點(diǎn):純消息廣播:BroadcastChannel只是一個(gè)消息通道它本身不提供任何狀態(tài)管理或同步機(jī)制。它只能通知其他 Tab 頁(yè)“某個(gè)事件發(fā)生了”或“某個(gè)數(shù)據(jù)可能已更新”但不能保證這些操作的原子性或順序性。無(wú)原生鎖定:同樣缺乏對(duì)共享資源的鎖定能力。如果多個(gè) Tab 頁(yè)都監(jiān)聽并嘗試響應(yīng)同一消息仍然可能出現(xiàn)競(jìng)態(tài)條件。強(qiáng)一致性挑戰(zhàn):適用于事件通知或非關(guān)鍵數(shù)據(jù)的同步但無(wú)法獨(dú)立保證對(duì)共享狀態(tài)的原子性更新。window.postMessage(配合window.opener或iframe):優(yōu)點(diǎn):允許跨窗口/框架通信。缺點(diǎn):限定通信目標(biāo):只能與opener窗口或iframe中的內(nèi)容通信不適用于任意 Tab 頁(yè)之間的廣播。復(fù)雜性:需要維護(hù)窗口引用處理消息來(lái)源。強(qiáng)一致性挑戰(zhàn):無(wú)法提供全局的協(xié)調(diào)和鎖定機(jī)制。IndexedDB:優(yōu)點(diǎn):客戶端結(jié)構(gòu)化存儲(chǔ)容量大支持事務(wù)。缺點(diǎn):事務(wù)粒度:IndexedDB事務(wù)僅在其自身范圍內(nèi)提供原子性。跨 Tab 頁(yè)的多個(gè)IndexedDB事務(wù)如果操作同一數(shù)據(jù)仍然可能需要額外的協(xié)調(diào)。復(fù)雜性:API 相對(duì)復(fù)雜直接用于通信不如專門的通信 API 方便。強(qiáng)一致性挑戰(zhàn):雖然其事務(wù)機(jī)制有助于數(shù)據(jù)完整性但要實(shí)現(xiàn)跨 Tab 頁(yè)的邏輯操作的強(qiáng)一致性仍需額外的同步原語(yǔ)。例如兩個(gè) Tab 頁(yè)各自在一個(gè)事務(wù)中讀取、修改、寫入同一個(gè)計(jì)數(shù)器沒有外部協(xié)調(diào)仍可能導(dǎo)致錯(cuò)誤。綜上所述傳統(tǒng)的通信手段在實(shí)現(xiàn)“強(qiáng)一致性”時(shí)力不從心主要癥結(jié)在于缺乏一個(gè)統(tǒng)一的協(xié)調(diào)中心和原子的鎖定機(jī)制。而這正是SharedWorker和Lock API能夠大放異彩的地方。SharedWorker跨 Tab 頁(yè)的中央?yún)f(xié)調(diào)者SharedWorker是 Web Worker 的一種特殊形式它可以在同源的所有瀏覽器 Tab 頁(yè)、窗口、iframe甚至其他SharedWorker之間共享。與普通的WebWorker也稱為DedicatedWorker不同DedicatedWorker每次加載頁(yè)面都會(huì)創(chuàng)建一個(gè)新的實(shí)例而SharedWorker在同一源下只會(huì)被實(shí)例化一次所有連接到它的上下文比如多個(gè) Tab 頁(yè)都會(huì)共享這同一個(gè)實(shí)例。SharedWorker 的核心特性單例模式:同一源下的所有頁(yè)面共享同一個(gè)SharedWorker實(shí)例。這使得它天然成為一個(gè)理想的中央?yún)f(xié)調(diào)者可以管理共享狀態(tài)、處理并發(fā)請(qǐng)求并廣播結(jié)果。獨(dú)立線程:SharedWorker運(yùn)行在獨(dú)立的線程中不會(huì)阻塞主線程保持頁(yè)面響應(yīng)性。端口通信:主線程或任何其他上下文通過MessagePort對(duì)象與SharedWorker進(jìn)行通信。每個(gè)連接到SharedWorker的上下文都會(huì)獲得一個(gè)獨(dú)立的MessagePort。持久性:只要有至少一個(gè) Tab 頁(yè)或窗口連接著SharedWorker它就會(huì)一直運(yùn)行。當(dāng)所有連接都關(guān)閉后SharedWorker也會(huì)被終止。SharedWorker 如何解決一致性問題作為中央?yún)f(xié)調(diào)者SharedWorker可以統(tǒng)一管理共享狀態(tài):所有跨 Tab 頁(yè)共享的數(shù)據(jù)都存儲(chǔ)在SharedWorker內(nèi)部。序列化操作:所有對(duì)共享狀態(tài)的修改請(qǐng)求都發(fā)送到SharedWorker。由于SharedWorker是單線程的它會(huì)按照接收到的順序或內(nèi)部調(diào)度策略依次處理這些請(qǐng)求從而避免內(nèi)部的競(jìng)態(tài)條件。廣播更新:當(dāng)共享狀態(tài)發(fā)生變化時(shí)SharedWorker可以通過其維護(hù)的MessagePort列表將最新的狀態(tài)廣播給所有連接的 Tab 頁(yè)確保所有 Tab 頁(yè)都及時(shí)獲取到一致的數(shù)據(jù)。然而SharedWorker自身并不能解決所有并發(fā)問題。例如如果多個(gè) Tab 頁(yè)同時(shí)向SharedWorker發(fā)送“請(qǐng)求修改”消息SharedWorker會(huì)按順序處理它們這解決了SharedWorker內(nèi)部的競(jìng)態(tài)。但如果在 Tab 頁(yè)發(fā)送請(qǐng)求之前需要進(jìn)行一些基于當(dāng)前共享狀態(tài)的預(yù)判斷或計(jì)算而這個(gè)判斷或計(jì)算的結(jié)果又可能被其他 Tab 頁(yè)在發(fā)送消息的間隙所改變那么仍然存在“讀-改-寫”的競(jìng)態(tài)條件。例如Tab A 和 Tab B 都想將一個(gè)計(jì)數(shù)器從 10 增加到 11。Tab A 讀取到計(jì)數(shù)器是 10。Tab B 讀取到計(jì)數(shù)器是 10。Tab A 計(jì)算出新值 11發(fā)送給SharedWorker。Tab B 計(jì)算出新值 11發(fā)送給SharedWorker。SharedWorker收到 Tab A 的請(qǐng)求將計(jì)數(shù)器設(shè)置為 11。SharedWorker收到 Tab B 的請(qǐng)求將計(jì)數(shù)器設(shè)置為 11。最終結(jié)果是 11而不是預(yù)期的 12。這就是我們需要Lock API來(lái)協(xié)調(diào)客戶端行為的原因。SharedWorker 基本代碼示例1.shared-worker.js(SharedWorker 腳本)// 定義一個(gè)用于存儲(chǔ)所有連接端口的數(shù)組 const ports []; // 示例共享狀態(tài) let sharedCounter 0; let sharedMessage Hello from SharedWorker!; /** * 廣播最新狀態(tài)給所有連接的客戶端 */ function broadcastState() { const state { counter: sharedCounter, message: sharedMessage }; ports.forEach(port { port.postMessage({ type: STATE_UPDATE, payload: state }); }); console.log([SharedWorker] State broadcasted:, state); } // 當(dāng)新的連接被建立時(shí)觸發(fā) self.onconnect (event) { const port event.ports[0]; // 獲取連接端口 ports.push(port); // 將端口添加到列表中 console.log([SharedWorker] New client connected. Total connections: ${ports.length}); // 向新連接的客戶端發(fā)送當(dāng)前狀態(tài) port.postMessage({ type: INITIAL_STATE, payload: { counter: sharedCounter, message: sharedMessage } }); // 監(jiān)聽來(lái)自客戶端的消息 port.onmessage (msgEvent) { const message msgEvent.data; console.log([SharedWorker] Received message from client:, message); switch (message.type) { case INCREMENT_COUNTER: sharedCounter; console.log([SharedWorker] Counter incremented to: ${sharedCounter}); broadcastState(); // 狀態(tài)更新后廣播 // 可以選擇向發(fā)送方回復(fù)確認(rèn)消息 port.postMessage({ type: INCREMENT_ACK, success: true, newCounter: sharedCounter }); break; case SET_MESSAGE: if (message.payload typeof message.payload string) { sharedMessage message.payload; console.log([SharedWorker] Message updated to: ${sharedMessage}); broadcastState(); // 狀態(tài)更新后廣播 port.postMessage({ type: SET_MESSAGE_ACK, success: true, newMessage: sharedMessage }); } else { port.postMessage({ type: SET_MESSAGE_ACK, success: false, error: Invalid message payload }); } break; case GET_CURRENT_STATE: port.postMessage({ type: CURRENT_STATE, payload: { counter: sharedCounter, message: sharedMessage } }); break; default: console.warn([SharedWorker] Unknown message type: ${message.type}); } }; // 監(jiān)聽端口斷開事件例如 Tab 頁(yè)關(guān)閉 port.onmessageerror (error) { console.error([SharedWorker] Message error on port:, error); // 通常不需要手動(dòng)移除因?yàn)?onclose 會(huì)處理 }; // 當(dāng)端口關(guān)閉時(shí)觸發(fā)例如 Tab 頁(yè)關(guān)閉 // 注意onclose 事件在某些瀏覽器中可能不會(huì)立即觸發(fā)或行為不一致 // 更可靠的斷開連接檢測(cè)通常依賴于主線程的錯(cuò)誤處理或心跳機(jī)制 // 不過對(duì)于 SharedWorker當(dāng)所有連接都斷開時(shí)worker 實(shí)例會(huì)被終止 // 因此這里無(wú)需顯式移除 port因?yàn)樗鼤?huì)自動(dòng)清理 // 實(shí)際應(yīng)用中如果需要精確管理連接數(shù)可以考慮更復(fù)雜的生命周期管理 // 例如port.onclose () { const index ports.indexOf(port); if (index -1) ports.splice(index, 1); console.log([SharedWorker] Client disconnected. Total connections: ${ports.length}); }; }; console.log([SharedWorker] Script loaded.);2.index.html(客戶端頁(yè)面)!DOCTYPE html html langen head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleSharedWorker Client/title style body { font-family: sans-serif; margin: 20px; } .container { border: 1px solid #ccc; padding: 15px; margin-bottom: 15px; border-radius: 5px; } button { padding: 8px 15px; margin-right: 10px; cursor: pointer; } input[typetext] { padding: 8px; width: 200px; margin-right: 10px; } #status { margin-top: 15px; padding: 10px; background-color: #f0f0f0; border-radius: 3px; } /style /head body h1SharedWorker 客戶端示例/h1 p打開多個(gè) Tab 頁(yè)觀察計(jì)數(shù)器和消息的同步。/p div classcontainer h2共享計(jì)數(shù)器/h2 p當(dāng)前計(jì)數(shù)器值: span idcounterValue0/span/p button idincrementButton增加計(jì)數(shù)器 (非強(qiáng)一致性)/button /div div classcontainer h2共享消息/h2 p當(dāng)前共享消息: span idmessageValueHello from SharedWorker!/span/p input typetext idmessageInput placeholder輸入新消息 button idsetMessageButton設(shè)置消息 (非強(qiáng)一致性)/button /div div idstatus h3事件日志:/h3 ul ideventLog/ul /div script const counterValueSpan document.getElementById(counterValue); const messageValueSpan document.getElementById(messageValue); const incrementButton document.getElementById(incrementButton); const messageInput document.getElementById(messageInput); const setMessageButton document.getElementById(setMessageButton); const eventLog document.getElementById(eventLog); let sharedWorker; let workerPort; function logEvent(message, type info) { const li document.createElement(li); li.textContent [${new Date().toLocaleTimeString()}] ${message}; if (type error) li.style.color red; eventLog.prepend(li); if (eventLog.children.length 20) { eventLog.removeChild(eventLog.lastChild); } } if (window.SharedWorker) { // 嘗試連接到 SharedWorker // URL 必須是同源的 sharedWorker new SharedWorker(shared-worker.js); workerPort sharedWorker.port; // 獲取端口對(duì)象 logEvent(嘗試連接到 SharedWorker...); // 監(jiān)聽來(lái)自 SharedWorker 的消息 workerPort.onmessage (event) { const message event.data; logEvent(收到 SharedWorker 消息: ${JSON.stringify(message)}); switch (message.type) { case INITIAL_STATE: case STATE_UPDATE: case CURRENT_STATE: counterValueSpan.textContent message.payload.counter; messageValueSpan.textContent message.payload.message; logEvent(更新頁(yè)面狀態(tài): 計(jì)數(shù)器${message.payload.counter}, 消息${message.payload.message}); break; case INCREMENT_ACK: if (message.success) { logEvent(計(jì)數(shù)器增加成功新值: ${message.newCounter}); } else { logEvent(計(jì)數(shù)器增加失敗: ${message.error}, error); } break; case SET_MESSAGE_ACK: if (message.success) { logEvent(消息設(shè)置成功新消息: ${message.newMessage}); } else { logEvent(消息設(shè)置失敗: ${message.error}, error); } break; default: logEvent(未知消息類型: ${message.type}); } }; workerPort.onerror (error) { logEvent(SharedWorker 錯(cuò)誤: ${error.message}, error); console.error(SharedWorker error:, error); }; // 啟動(dòng)端口通信 workerPort.start(); logEvent(SharedWorker 端口已啟動(dòng)。); } else { logEvent(您的瀏覽器不支持 SharedWorker。, error); alert(您的瀏覽器不支持 SharedWorker。); } incrementButton.addEventListener(click, () { if (workerPort) { logEvent(發(fā)送 INCREMENT_COUNTER 請(qǐng)求...); workerPort.postMessage({ type: INCREMENT_COUNTER }); } }); setMessageButton.addEventListener(click, () { if (workerPort) { const newMessage messageInput.value.trim(); if (newMessage) { logEvent(發(fā)送 SET_MESSAGE 請(qǐng)求: ${newMessage}...); workerPort.postMessage({ type: SET_MESSAGE, payload: newMessage }); } else { logEvent(請(qǐng)?zhí)顚懸O(shè)置的消息。, warn); } } }); // 首次加載時(shí)請(qǐng)求當(dāng)前狀態(tài)以防 SharedWorker 已經(jīng)運(yùn)行 window.addEventListener(load, () { if (workerPort) { workerPort.postMessage({ type: GET_CURRENT_STATE }); } }); /script /body /html在上述示例中我們創(chuàng)建了一個(gè)SharedWorker來(lái)管理sharedCounter和sharedMessage。所有連接的 Tab 頁(yè)都可以發(fā)送請(qǐng)求來(lái)修改這些狀態(tài)SharedWorker會(huì)處理這些請(qǐng)求并廣播最新的狀態(tài)給所有 Tab 頁(yè)。然而正如前面提到的如果 Tab 頁(yè)在發(fā)送INCREMENT_COUNTER之前需要基于sharedCounter的值進(jìn)行一些復(fù)雜判斷并且這個(gè)判斷和發(fā)送消息之間存在時(shí)間差那么仍然可能出現(xiàn)問題。這就是Lock API發(fā)揮作用的地方。Web Locks API (Lock API)瀏覽器原生的原子鎖Web Locks API通常簡(jiǎn)稱為L(zhǎng)ock API提供了一種在同源內(nèi)所有上下文包括 Tab 頁(yè)、窗口、iframe和 Web Worker之間協(xié)調(diào)對(duì)共享資源訪問的機(jī)制。它允許開發(fā)者請(qǐng)求一個(gè)帶名稱的鎖并保證在鎖被持有期間沒有其他上下文可以獲取到同名的獨(dú)占鎖。Lock API 的核心特性原子性保證:Lock API是瀏覽器原生的它確保鎖的獲取是原子的。一旦一個(gè)上下文成功獲取到獨(dú)占鎖其他上下文就無(wú)法獲取同名獨(dú)占鎖直到鎖被釋放。命名鎖:鎖通過字符串名稱進(jìn)行標(biāo)識(shí)。只要名稱相同不同上下文之間就能競(jìng)爭(zhēng)同一個(gè)鎖。作用域:鎖的作用域限定在同一個(gè)源 (origin) 內(nèi)。兩種模式:exclusive(獨(dú)占模式):這是默認(rèn)模式。一旦一個(gè)上下文獲得了獨(dú)占鎖其他上下文就不能獲得同名的任何鎖無(wú)論是獨(dú)占還是共享直到該獨(dú)占鎖被釋放。適用于寫操作。shared(共享模式):多個(gè)上下文可以同時(shí)獲取同名的共享鎖。但如果有一個(gè)上下文嘗試獲取同名的獨(dú)占鎖它必須等待所有共享鎖都被釋放。適用于讀操作。request()方法:navigator.locks.request(name, [options,] callback)是核心方法。它返回一個(gè) Promise該 Promise 在鎖被成功獲取后解決并在callback函數(shù)執(zhí)行完畢或 Promise 解決/拒絕后自動(dòng)釋放鎖。自動(dòng)釋放:最強(qiáng)大的特性之一是當(dāng)獲取鎖的上下文Tab 頁(yè)或 Worker關(guān)閉或發(fā)生導(dǎo)航時(shí)瀏覽器會(huì)自動(dòng)釋放該上下文持有的所有鎖。這大大降低了死鎖的風(fēng)險(xiǎn)。Lock API 如何解決一致性問題Lock API提供了我們夢(mèng)寐以求的“互斥訪問”能力。通過在關(guān)鍵代碼塊即所謂的“臨界區(qū)”前后請(qǐng)求和釋放鎖我們可以確保在任何給定時(shí)間只有一個(gè)上下文能夠執(zhí)行該臨界區(qū)的代碼。結(jié)合SharedWorkerLock API的作用是協(xié)調(diào)多個(gè)客戶端對(duì)SharedWorker內(nèi)部狀態(tài)修改操作的請(qǐng)求。即在客戶端發(fā)送可能導(dǎo)致競(jìng)態(tài)的消息之前先通過Lock API獲得一個(gè)獨(dú)占鎖。場(chǎng)景示例:Tab A想要執(zhí)行一個(gè)需要強(qiáng)一致性的操作例如基于當(dāng)前計(jì)數(shù)器的值進(jìn)行復(fù)雜計(jì)算后更新計(jì)數(shù)器。Tab A調(diào)用navigator.locks.request(my_resource_lock, ...)。如果鎖可用Tab A成功獲取鎖然后執(zhí)行其臨界區(qū)代碼向SharedWorker發(fā)送消息請(qǐng)求獲取當(dāng)前狀態(tài)如果需要?;讷@取到的狀態(tài)進(jìn)行計(jì)算。向SharedWorker發(fā)送消息請(qǐng)求更新狀態(tài)。等待SharedWorker的確認(rèn)或狀態(tài)更新廣播。在Tab A持有鎖期間如果Tab B也嘗試獲取my_resource_lock它將必須等待。Tab A的操作完成后request回調(diào)函數(shù)執(zhí)行完畢或其內(nèi)部 Promise 解決鎖會(huì)自動(dòng)釋放。Tab B隨后可以獲取鎖并執(zhí)行其操作。通過這種方式即使SharedWorker內(nèi)部是單線程處理請(qǐng)求外部客戶端在發(fā)起請(qǐng)求之前也通過Lock API進(jìn)行了協(xié)調(diào)從而保證了整個(gè)“讀-改-寫”或“操作-更新”流程的原子性和強(qiáng)一致性。Lock API 基本代碼示例async function doSomethingWithLock(resourceName, operation) { console.log([Tab] 嘗試獲取獨(dú)占鎖 ${resourceName}...); try { await navigator.locks.request(resourceName, { mode: exclusive, ifAvailable: false, steal: false, signal: AbortSignal.timeout(5000) }, async (lock) { // lock 對(duì)象本身沒有太多直接用途它的存在表示你持有了鎖 if (lock) { console.log([Tab] 成功獲取獨(dú)占鎖 ${resourceName}。); try { // 執(zhí)行臨界區(qū)操作 await operation(); } catch (opError) { console.error([Tab] 臨界區(qū)操作失敗:, opError); throw opError; // 重新拋出讓外層捕獲 } finally { console.log([Tab] 獨(dú)占鎖 ${resourceName} 釋放中...); // 鎖會(huì)在 callback 結(jié)束時(shí)自動(dòng)釋放無(wú)需手動(dòng)調(diào)用 release } } else { // ifAvailable: true 時(shí)可能發(fā)生但我們?cè)O(shè)置為 false console.warn([Tab] 未能獲取獨(dú)占鎖 ${resourceName} (意外情況因?yàn)?ifAvailablefalse)); } }); console.log([Tab] 獨(dú)占鎖 ${resourceName} 已釋放。); } catch (error) { if (error.name AbortError) { console.warn([Tab] 獲取鎖 ${resourceName} 超時(shí)或被中止。); } else { console.error([Tab] 獲取鎖或執(zhí)行操作時(shí)發(fā)生錯(cuò)誤:, error); } throw error; // 重新拋出錯(cuò)誤以便調(diào)用者處理 } } // 示例使用 async function exampleUsage() { await doSomethingWithLock(my_shared_resource, async () { // 模擬一個(gè)耗時(shí)且需要獨(dú)占訪問的操作 console.log([Tab] 獨(dú)占操作開始...); await new Promise(resolve setTimeout(resolve, 2000)); console.log([Tab] 獨(dú)占操作完成。); }); } // exampleUsage(); // 調(diào)用此函數(shù)來(lái)測(cè)試navigator.locks.request參數(shù)說(shuō)明:參數(shù)名類型描述namestring鎖的名稱。這是識(shí)別和競(jìng)爭(zhēng)鎖的關(guān)鍵。optionsobject可選對(duì)象用于配置鎖的行為。modestring鎖的模式。exclusive(默認(rèn)) 或shared。ifAvailableboolean如果設(shè)置為true則在鎖不可用時(shí)Promise 會(huì)立即以u(píng)ndefined解決而不是等待。默認(rèn)false。stealboolean如果設(shè)置為true且鎖不可用則會(huì)嘗試“竊取”當(dāng)前持有的鎖。只有當(dāng)當(dāng)前鎖持有者是一個(gè)非活動(dòng)的 Tab 頁(yè)或 Worker 時(shí)竊取才可能成功。使用時(shí)需謹(jǐn)慎可能導(dǎo)致數(shù)據(jù)不一致。默認(rèn)false。signalAbortSignal允許你通過一個(gè)AbortController實(shí)例來(lái)取消鎖的獲取請(qǐng)求。如果signal被觸發(fā)請(qǐng)求鎖的 Promise 將會(huì)以AbortError拒絕。常用于設(shè)置超時(shí)。callbackFunction一個(gè)異步回調(diào)函數(shù)當(dāng)鎖被成功獲取后執(zhí)行。它接收一個(gè)lock對(duì)象作為參數(shù)通常不需要直接使用。該函數(shù)返回的 Promise 解決后鎖會(huì)自動(dòng)釋放?;?SharedWorker 與 Lock API 的鎖競(jìng)爭(zhēng)實(shí)現(xiàn)強(qiáng)一致性通信現(xiàn)在我們有了SharedWorker作為統(tǒng)一的狀態(tài)管理者和消息分發(fā)中心以及Lock API作為客戶端協(xié)調(diào)并發(fā)請(qǐng)求的原子鎖。是時(shí)候?qū)⑺鼈兘Y(jié)合起來(lái)構(gòu)建一個(gè)真正的強(qiáng)一致性通信方案了。核心思想與工作流程SharedWorker 作為唯一數(shù)據(jù)源:所有的共享狀態(tài)都只在SharedWorker內(nèi)部維護(hù)。客戶端通過 Lock API 協(xié)調(diào)請(qǐng)求:當(dāng)任何客戶端Tab 頁(yè)需要執(zhí)行一個(gè)涉及共享狀態(tài)修改的“臨界操作”時(shí)它必須首先請(qǐng)求一個(gè)獨(dú)占鎖。鎖內(nèi)操作與 SharedWorker 交互:只有在成功獲取鎖之后客戶端才能向SharedWorker發(fā)送修改請(qǐng)求。這個(gè)請(qǐng)求可能包含讀取當(dāng)前狀態(tài)、基于當(dāng)前狀態(tài)計(jì)算新值、然后提交新值的整個(gè)流程。SharedWorker 處理并廣播:SharedWorker接收到請(qǐng)求后更新其內(nèi)部狀態(tài)然后將最新的狀態(tài)廣播給所有連接的客戶端??蛻舳酸尫沛i:客戶端在收到SharedWorker的確認(rèn)或狀態(tài)更新廣播后或者在臨界操作完成后鎖會(huì)自動(dòng)釋放。通過這種模式我們確保了在任何時(shí)刻只有一個(gè)客戶端能夠發(fā)起對(duì)共享狀態(tài)的原子性修改請(qǐng)求。SharedWorker作為單線程實(shí)體其內(nèi)部狀態(tài)的修改總是順序執(zhí)行的。所有客戶端都能通過SharedWorker實(shí)時(shí)獲取到最新的、一致的共享狀態(tài)。代碼示例強(qiáng)一致性計(jì)數(shù)器與消息管理我們將修改之前的客戶端代碼為計(jì)數(shù)器增加一個(gè)強(qiáng)一致性的操作。1.shared-worker.js(保持不變它只負(fù)責(zé)處理收到的請(qǐng)求和廣播狀態(tài))// shared-worker.js 與之前相同 const ports []; let sharedCounter 0; let sharedMessage Hello from SharedWorker!; function broadcastState() { const state { counter: sharedCounter, message: sharedMessage }; ports.forEach(port { port.postMessage({ type: STATE_UPDATE, payload: state }); }); console.log([SharedWorker] State broadcasted:, state); } self.onconnect (event) { const port event.ports[0]; ports.push(port); console.log([SharedWorker] New client connected. Total connections: ${ports.length}); port.postMessage({ type: INITIAL_STATE, payload: { counter: sharedCounter, message: sharedMessage } }); port.onmessage async (msgEvent) { // 注意這里改為 async以便等待處理 const message msgEvent.data; console.log([SharedWorker] Received message from client:, message); switch (message.type) { case INCREMENT_COUNTER: // SharedWorker 內(nèi)部是單線程的所以這里不需要 Lock API // 收到請(qǐng)求就直接處理確保內(nèi)部狀態(tài)更新的原子性 sharedCounter; console.log([SharedWorker] Counter incremented to: ${sharedCounter}); broadcastState(); port.postMessage({ type: INCREMENT_ACK, success: true, newCounter: sharedCounter }); break; case INCREMENT_COUNTER_COMPLEX: // 對(duì)于更復(fù)雜的更新SharedWorker 也可以提供一個(gè)確認(rèn)機(jī)制 // 這里模擬一個(gè)帶條件的復(fù)雜遞增 if (sharedCounter message.payload.maxLimit) { sharedCounter message.payload.value; console.log([SharedWorker] Complex counter incremented by ${message.payload.value} to: ${sharedCounter}); broadcastState(); port.postMessage({ type: INCREMENT_COMPLEX_ACK, success: true, newCounter: sharedCounter }); } else { console.warn([SharedWorker] Complex increment failed: max limit reached (${message.payload.maxLimit})); port.postMessage({ type: INCREMENT_COMPLEX_ACK, success: false, error: Max limit reached, currentCounter: sharedCounter }); } break; case SET_MESSAGE: if (message.payload typeof message.payload string) { sharedMessage message.payload; console.log([SharedWorker] Message updated to: ${sharedMessage}); broadcastState(); port.postMessage({ type: SET_MESSAGE_ACK, success: true, newMessage: sharedMessage }); } else { port.postMessage({ type: SET_MESSAGE_ACK, success: false, error: Invalid message payload }); } break; case GET_CURRENT_STATE: port.postMessage({ type: CURRENT_STATE, payload: { counter: sharedCounter, message: sharedMessage } }); break; default: console.warn([SharedWorker] Unknown message type: ${message.type}); } }; }; console.log([SharedWorker] Script loaded.);2.index.html(客戶端頁(yè)面增加強(qiáng)一致性操作按鈕)!DOCTYPE html html langen head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleSharedWorker Lock API Client/title style body { font-family: sans-serif; margin: 20px; } .container { border: 1px solid #ccc; padding: 15px; margin-bottom: 15px; border-radius: 5px; } button { padding: 8px 15px; margin-right: 10px; cursor: pointer; } input[typetext] { padding: 8px; width: 200px; margin-right: 10px; } input[typenumber] { padding: 8px; width: 80px; margin-right: 10px; } #status { margin-top: 15px; padding: 10px; background-color: #f0f0f0; border-radius: 3px; } .error { color: red; } /style /head body h1SharedWorker Lock API 客戶端示例/h1 p打開多個(gè) Tab 頁(yè)通過鎖定機(jī)制協(xié)調(diào)對(duì)共享數(shù)據(jù)的更新。/p div classcontainer h2共享計(jì)數(shù)器/h2 p當(dāng)前計(jì)數(shù)器值: span idcounterValue0/span/p button idincrementButton增加計(jì)數(shù)器 (非強(qiáng)一致性)/button hr h3強(qiáng)一致性遞增 (使用 Lock API)/h3 p遞增值: input typenumber idincrementAmount value1 min1/p p最大限制: input typenumber idmaxLimit value10 min1/p button idincrementStronglyButton強(qiáng)一致性遞增/button span idstrongIncrementStatus stylemargin-left: 10px;/span /div div classcontainer h2共享消息/h2 p當(dāng)前共享消息: span idmessageValueHello from SharedWorker!/span/p input typetext idmessageInput placeholder輸入新消息 button idsetMessageButton設(shè)置消息 (非強(qiáng)一致性)/button /div div idstatus h3事件日志:/h3 ul ideventLog/ul /div script const counterValueSpan document.getElementById(counterValue); const messageValueSpan document.getElementById(messageValue); const incrementButton document.getElementById(incrementButton); const incrementAmountInput document.getElementById(incrementAmount); const maxLimitInput document.getElementById(maxLimit); const incrementStronglyButton document.getElementById(incrementStronglyButton); const strongIncrementStatus document.getElementById(strongIncrementStatus); const messageInput document.getElementById(messageInput); const setMessageButton document.getElementById(setMessageButton); const eventLog document.getElementById(eventLog); let sharedWorker; let workerPort; let currentCounterState 0; // 客戶端維護(hù)的最新計(jì)數(shù)器狀態(tài) function logEvent(message, type info) { const li document.createElement(li); li.textContent [${new Date().toLocaleTimeString()}] ${message}; if (type error) li.classList.add(error); if (type warn) li.style.color orange; eventLog.prepend(li); if (eventLog.children.length 20) { eventLog.removeChild(eventLog.lastChild); } } if (window.SharedWorker navigator.locks) { sharedWorker new SharedWorker(shared-worker.js); workerPort sharedWorker.port; logEvent(嘗試連接到 SharedWorker...); workerPort.onmessage (event) { const message event.data; // logEvent(收到 SharedWorker 消息: ${JSON.stringify(message)}); // 調(diào)試時(shí)開啟 switch (message.type) { case INITIAL_STATE: case STATE_UPDATE: case CURRENT_STATE: currentCounterState message.payload.counter; // 更新客戶端維護(hù)的最新狀態(tài) counterValueSpan.textContent message.payload.counter; messageValueSpan.textContent message.payload.message; logEvent(更新頁(yè)面狀態(tài): 計(jì)數(shù)器${message.payload.counter}, 消息${message.payload.message}); break; case INCREMENT_ACK: if (message.success) { logEvent(計(jì)數(shù)器非強(qiáng)一致性增加成功新值: ${message.newCounter}); } else { logEvent(計(jì)數(shù)器非強(qiáng)一致性增加失敗: ${message.error}, error); } break; case INCREMENT_COMPLEX_ACK: // 此 ACK 消息通常是在 Lock API 內(nèi)部等待的這里只是為了演示 // 實(shí)際邏輯中Lock API 的 Promise 解決就代表操作完成 if (message.success) { logEvent(計(jì)數(shù)器強(qiáng)一致性增加成功新值: ${message.newCounter}); strongIncrementStatus.textContent 成功! 新值: ${message.newCounter}; strongIncrementStatus.style.color green; } else { logEvent(計(jì)數(shù)器強(qiáng)一致性增加失敗: ${message.error} (當(dāng)前值: ${message.currentCounter}), error); strongIncrementStatus.textContent 失敗: ${message.error}; strongIncrementStatus.style.color red; } break; case SET_MESSAGE_ACK: if (message.success) { logEvent(消息設(shè)置成功新消息: ${message.newMessage}); } else { logEvent(消息設(shè)置失敗: ${message.error}, error); } break; default: logEvent(未知消息類型: ${message.type}); } }; workerPort.onerror (error) { logEvent(SharedWorker 錯(cuò)誤: ${error.message}, error); console.error(SharedWorker error:, error); }; workerPort.start(); logEvent(SharedWorker 端口已啟動(dòng)。); } else { logEvent(您的瀏覽器不支持 SharedWorker 或 Lock API。, error); alert(您的瀏覽器不支持 SharedWorker 或 Lock API。); } // --- 非強(qiáng)一致性操作 --- incrementButton.addEventListener(click, () { if (workerPort) { logEvent(發(fā)送 INCREMENT_COUNTER 請(qǐng)求 (非強(qiáng)一致性)...); workerPort.postMessage({ type: INCREMENT_COUNTER }); } }); setMessageButton.addEventListener(click, () { if (workerPort) { const newMessage messageInput.value.trim(); if (newMessage) { logEvent(發(fā)送 SET_MESSAGE 請(qǐng)求 (非強(qiáng)一致性): ${newMessage}...); workerPort.postMessage({ type: SET_MESSAGE, payload: newMessage }); } else { logEvent(請(qǐng)?zhí)顚懸O(shè)置的消息。, warn); } } }); // --- 強(qiáng)一致性操作 --- incrementStronglyButton.addEventListener(click, async () { if (!workerPort) { logEvent(SharedWorker 未連接。, error); return; } const incrementAmount parseInt(incrementAmountInput.value, 10); const maxLimit parseInt(maxLimitInput.value, 10); if (isNaN(incrementAmount) || incrementAmount 0) { logEvent(遞增值必須是大于0的數(shù)字。, warn); return; } if (isNaN(maxLimit) || maxLimit 0) { logEvent(最大限制必須是大于0的數(shù)字。, warn); return; } strongIncrementStatus.textContent 嘗試獲取鎖...; strongIncrementStatus.style.color gray; logEvent([Tab] 嘗試獲取獨(dú)占鎖 counter_update_lock 來(lái)執(zhí)行強(qiáng)一致性遞增...); try { // 使用 Lock API 請(qǐng)求獨(dú)占鎖 // signal: AbortSignal.timeout(5000) 設(shè)定5秒超時(shí)防止無(wú)限等待 await navigator.locks.request(counter_update_lock, { mode: exclusive, signal: AbortSignal.timeout(5000) }, async (lock) { // lock 參數(shù)的存在表示我們成功獲得了鎖 if (!lock) { // 理論上不會(huì)發(fā)生因?yàn)槲覀儧]有設(shè)置 ifAvailable: true logEvent([Tab] 意外未能獲取獨(dú)占鎖 counter_update_lock。, error); strongIncrementStatus.textContent 獲取鎖失敗 (意外); strongIncrementStatus.style.color red; return; } logEvent([Tab] 成功獲取獨(dú)占鎖 counter_update_lock。開始執(zhí)行臨界區(qū)操作...); strongIncrementStatus.textContent 獲取鎖成功正在操作...; strongIncrementStatus.style.color blue; // 臨界區(qū)開始在這里執(zhí)行需要強(qiáng)一致性的操作 // 1. 發(fā)送消息給 SharedWorker 請(qǐng)求更新 // 2. 等待 SharedWorker 的確認(rèn)消息 await new Promise((resolve, reject) { const messageHandler (event) { const response event.data; if (response.type INCREMENT_COMPLEX_ACK) { workerPort.removeEventListener(message, messageHandler); // 移除監(jiān)聽器 if (response.success) { resolve(response); } else { reject(new Error(response.error || 強(qiáng)一致性遞增失敗)); } } }; workerPort.addEventListener(message, messageHandler); workerPort.postMessage({ type: INCREMENT_COUNTER_COMPLEX, payload: { value: incrementAmount, maxLimit: maxLimit } }); logEvent([Tab] 發(fā)送強(qiáng)一致性遞增請(qǐng)求 (遞增值: ${incrementAmount}, 最大限制: ${maxLimit})...); }); logEvent([Tab] 強(qiáng)一致性遞增操作完成。鎖即將自動(dòng)釋放。); }); // Lock API 的 Promise 解決時(shí)表示鎖已釋放且臨界區(qū)操作成功 logEvent([Tab] 獨(dú)占鎖 counter_update_lock 已釋放。, info); } catch (error) { if (error.name AbortError) { logEvent([Tab] 獲取鎖 counter_update_lock 超時(shí)或被中止。, warn); strongIncrementStatus.textContent 獲取鎖超時(shí)或被取消; strongIncrementStatus.style.color orange; } else { logEvent([Tab] 強(qiáng)一致性遞增操作失敗: ${error.message}, error); strongIncrementStatus.textContent 操作失敗: ${error.message}; strongIncrementStatus.style.color red; } console.error([Tab] 強(qiáng)一致性遞增錯(cuò)誤:, error); } finally { // 無(wú)論成功失敗確保狀態(tài)顯示正確 // 最終的 counterValueSpan 會(huì)通過 SharedWorker 的 STATE_UPDATE 消息更新 incrementStronglyButton.disabled false; // 重新啟用按鈕 } }); // 首次加載時(shí)請(qǐng)求當(dāng)前狀態(tài)以防 SharedWorker 已經(jīng)運(yùn)行 window.addEventListener(load, () { if (workerPort) { workerPort.postMessage({ type: GET_CURRENT_STATE }); } }); /script /body /html在上述index.html示例中我們新增了一個(gè)“強(qiáng)一致性遞增”按鈕。當(dāng)用戶點(diǎn)擊此按鈕時(shí)客戶端首先使用navigator.locks.request(counter_update_lock, ...)嘗試獲取一個(gè)名為counter_update_lock的獨(dú)占鎖。AbortSignal.timeout(5000)用于設(shè)置鎖獲取的超時(shí)時(shí)間防止因其他 Tab 頁(yè)長(zhǎng)時(shí)間持有鎖而導(dǎo)致當(dāng)前 Tab 頁(yè)無(wú)限等待。一旦成功獲取鎖lock參數(shù)存在客戶端進(jìn)入臨界區(qū)。在臨界區(qū)內(nèi)客戶端向SharedWorker發(fā)送一個(gè)INCREMENT_COUNTER_COMPLEX消息。這個(gè)消息包含了遞增值和最大限制讓SharedWorker執(zhí)行一個(gè)帶條件的遞增操作??蛻舳送ㄟ^監(jiān)聽SharedWorker的消息等待INCREMENT_COMPLEX_ACK響應(yīng)。這是一個(gè)Promise包裝的等待過程確??蛻舳嗽阪i釋放前已經(jīng)知道SharedWorker的操作結(jié)果。SharedWorker處理請(qǐng)求更新sharedCounter并廣播STATE_UPDATE給所有客戶端??蛻舳耸盏絀NCREMENT_COMPLEX_ACK消息后Promise解決。navigator.locks.request的回調(diào)函數(shù)執(zhí)行完畢瀏覽器自動(dòng)釋放counter_update_lock。其他等待該鎖的 Tab 頁(yè)現(xiàn)在可以嘗試獲取它。通過這種機(jī)制即使多個(gè) Tab 頁(yè)同時(shí)點(diǎn)擊“強(qiáng)一致性遞增”按鈕它們也會(huì)按照順序一個(gè)接一個(gè)地獲取鎖向SharedWorker發(fā)送請(qǐng)求從而確保每次遞增操作都是原子且基于最新的共享狀態(tài)進(jìn)行的。例如如果計(jì)數(shù)器最大限制是10當(dāng)前是9兩個(gè)Tab頁(yè)同時(shí)請(qǐng)求遞增2。只有一個(gè)Tab頁(yè)能獲取鎖它發(fā)送請(qǐng)求SharedWorker處理后發(fā)現(xiàn)9211 10則拒絕并廣播當(dāng)前狀態(tài)9。另一個(gè)Tab頁(yè)獲取鎖后發(fā)現(xiàn)當(dāng)前是9同樣拒絕。這樣就避免了計(jì)數(shù)器超出限制或產(chǎn)生錯(cuò)誤計(jì)算。進(jìn)階場(chǎng)景與考量共享鎖 (Shared Lock) 用于讀操作在某些情況下我們可能希望多個(gè) Tab 頁(yè)可以同時(shí)讀取共享資源但只有在寫入時(shí)才需要獨(dú)占訪問。這時(shí)可以使用shared模式的鎖。讀操作:navigator.locks.request(my_resource, { mode: shared }, async (lock) { /* 讀取操作 */ });寫操作:navigator.locks.request(my_resource, { mode: exclusive }, async (lock) { /* 寫入操作 */ });當(dāng)有共享鎖被持有獨(dú)占鎖的請(qǐng)求會(huì)等待。當(dāng)獨(dú)占鎖被持有任何共享鎖或獨(dú)占鎖的請(qǐng)求都會(huì)等待。這提供了一種經(jīng)典的讀寫鎖模型。新 Tab 頁(yè)加載時(shí)的狀態(tài)同步當(dāng)一個(gè)新的 Tab 頁(yè)打開并連接到SharedWorker時(shí)它需要立即獲取當(dāng)前的最新狀態(tài)。在我們的示例中SharedWorker在onconnect事件中立即發(fā)送INITIAL_STATE消息或者客戶端在window.load時(shí)發(fā)送GET_CURRENT_STATE請(qǐng)求都能實(shí)現(xiàn)這一點(diǎn)。錯(cuò)誤處理與魯棒性AbortSignal:在navigator.locks.request中使用AbortSignal.timeout()是非常重要的可以防止鎖請(qǐng)求無(wú)限等待提升用戶體驗(yàn)。try...catch:始終在異步操作包括鎖的獲取和臨界區(qū)內(nèi)的操作中使用try...catch塊來(lái)捕獲和處理錯(cuò)誤。SharedWorker 崩潰:如果SharedWorker腳本本身出現(xiàn)未捕獲的錯(cuò)誤導(dǎo)致崩潰所有連接到它的port都會(huì)收到錯(cuò)誤事件??蛻舳诵枰兄剡B或降級(jí)策略。幸運(yùn)的是SharedWorker崩潰時(shí)瀏覽器會(huì)自動(dòng)釋放所有由其客戶端持有的鎖避免死鎖。Tab 頁(yè)崩潰/關(guān)閉:Lock API的一個(gè)強(qiáng)大之處在于如果持有鎖的 Tab 頁(yè)意外關(guān)閉或?qū)Ш诫x開瀏覽器會(huì)自動(dòng)釋放該 Tab 頁(yè)持有的所有鎖這有效防止了死鎖。性能考量雖然SharedWorker和Lock API提供了強(qiáng)大的同步能力但它們并非沒有開銷。消息傳遞開銷:postMessage涉及數(shù)據(jù)的序列化和反序列化。對(duì)于大量或復(fù)雜的數(shù)據(jù)這可能產(chǎn)生性能負(fù)擔(dān)。鎖競(jìng)爭(zhēng)開銷:頻繁的鎖競(jìng)爭(zhēng)會(huì)導(dǎo)致一些 Tab 頁(yè)等待這在高并發(fā)場(chǎng)景下可能影響響應(yīng)速度。權(quán)衡:這種機(jī)制最適用于需要嚴(yán)格一致性的關(guān)鍵業(yè)務(wù)操作。對(duì)于非核心、允許最終一致性的數(shù)據(jù)同步BroadcastChannel或localStorage配合事件監(jiān)聽可能更簡(jiǎn)單高效。替代方案與混合模式IndexedDBLock API:如果共享狀態(tài)需要持久化并且SharedWorker的生命周期不夠例如用戶關(guān)閉所有 Tab 頁(yè)后狀態(tài)丟失可以將IndexedDB作為后端存儲(chǔ)然后使用Lock API來(lái)協(xié)調(diào)對(duì)IndexedDB事務(wù)的訪問。SharedWorker依然可以作為協(xié)調(diào)者但數(shù)據(jù)源變?yōu)镮ndexedDB。服務(wù)器端同步:對(duì)于需要跨瀏覽器、跨設(shè)備甚至跨用戶的數(shù)據(jù)一致性服務(wù)器端同步是不可避免的。SharedWorker和Lock API解決的是單個(gè)用戶在同一瀏覽器中的一致性問題??偨Y(jié)與展望SharedWorker和Web Locks API是現(xiàn)代 Web 平臺(tái)提供的強(qiáng)大工具它們共同為前端開發(fā)者解決跨 Tab 頁(yè)強(qiáng)一致性通信這一復(fù)雜挑戰(zhàn)提供了可靠的方案。SharedWorker作為中心化的狀態(tài)管理和消息分發(fā)樞紐確保了數(shù)據(jù)源的唯一性和操作的序列化而Lock API則為客戶端提供了原子的互斥訪問機(jī)制有效防止了競(jìng)態(tài)條件保證了“讀-改-寫”等臨界操作的完整性。通過本文的深入探討和代碼示例我們已經(jīng)掌握了如何利用這兩個(gè) API 構(gòu)建一個(gè)健壯的、高一致性的跨 Tab 頁(yè)通信系統(tǒng)。在實(shí)際應(yīng)用中開發(fā)者應(yīng)根據(jù)業(yè)務(wù)需求權(quán)衡性能與一致性選擇最合適的同步策略。理解并熟練運(yùn)用這些技術(shù)將使我們能夠構(gòu)建出更加復(fù)雜、更加穩(wěn)定的富客戶端 Web 應(yīng)用。隨著 Web 平臺(tái)能力的不斷增強(qiáng)期待未來(lái)有更多創(chuàng)新的解決方案涌現(xiàn)進(jìn)一步提升用戶體驗(yàn)和開發(fā)效率。
版權(quán)聲明: 本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)聯(lián)系我們進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

網(wǎng)站流量檢測(cè)可以看的網(wǎng)站都有哪些

網(wǎng)站流量檢測(cè),可以看的網(wǎng)站都有哪些,wordpress中數(shù)據(jù)庫(kù)配置文件,無(wú)錫梅村網(wǎng)站建設(shè)3步搭建Sunshine游戲串流#xff1a;從入門到專業(yè)的完整教程 【免費(fèi)下載鏈接】Sunshine Suns

2026/01/23 03:08:01

常州市城投建設(shè)工程招標(biāo)有限公司網(wǎng)站江門網(wǎng)站定制多少錢

常州市城投建設(shè)工程招標(biāo)有限公司網(wǎng)站,江門網(wǎng)站定制多少錢,網(wǎng)站空間不夠用怎么辦,wordpress價(jià)格比較模板網(wǎng)絡(luò)安全審計(jì)是一種檢查和評(píng)估網(wǎng)絡(luò)安全控制措施、策略和程序的有效性的過程。網(wǎng)絡(luò)安全審計(jì)的目標(biāo)是

2026/01/23 02:34:01

大連設(shè)計(jì)網(wǎng)站公司seo基礎(chǔ)入門免費(fèi)教程

大連設(shè)計(jì)網(wǎng)站公司,seo基礎(chǔ)入門免費(fèi)教程,南通市港閘區(qū)城鄉(xiāng)建設(shè)局網(wǎng)站,鎮(zhèn)江企業(yè)做網(wǎng)站DaVinci Resolve調(diào)色完成后導(dǎo)出供HeyGem使用的最佳參數(shù) 在數(shù)字人視頻生成日益普及的今天#xff0c

2026/01/23 18:28:01

網(wǎng)站開發(fā)用px好還是em好石家莊 最新

網(wǎng)站開發(fā)用px好還是em好,石家莊 最新,贛榆做網(wǎng)站,海珠企業(yè)網(wǎng)站建設(shè)顯卡內(nèi)存檢測(cè)#xff1a;5分鐘快速診斷你的顯卡健康狀態(tài)#xff01;#x1f680; 【免費(fèi)下載鏈接】memtest_vulka

2026/01/23 02:44:01