面試必備:WebWorker 處理復(fù)雜場(chǎng)景下卡頓問(wèn)題!
Hello,大家好,我是 Sunday
這段時(shí)間有不少同學(xué)在面試中遇到了 web worker 的問(wèn)題。所以今天咱們就通過(guò)這篇文章,把 web worker 搞明白!
Web Worker 到底是什么?
因?yàn)?JS 是單線(xiàn)程(主線(xiàn)程)的,這意味著它一次只能做一件事。
當(dāng)你在瀏覽器中執(zhí)行大量的計(jì)算任務(wù)(如數(shù)據(jù)處理、復(fù)雜算法等)時(shí),主線(xiàn)程會(huì)被阻塞,導(dǎo)致頁(yè)面無(wú)法響應(yīng)用戶(hù)交互,影響用戶(hù)體驗(yàn)。換句話(huà)說(shuō),當(dāng)主線(xiàn)程上執(zhí)行耗時(shí)任務(wù)時(shí),UI 會(huì)被卡死,用戶(hù)無(wú)法進(jìn)行正常的操作。
例如,假設(shè)我們要執(zhí)行一個(gè)耗時(shí)的任務(wù),比如對(duì)一個(gè)非常大的數(shù)據(jù)集合進(jìn)行排序或計(jì)算。在主線(xiàn)程上執(zhí)行這類(lèi)任務(wù)時(shí),頁(yè)面會(huì)變得不響應(yīng),直到任務(wù)完成后,UI 才會(huì)刷新。
<div class="box">hello</div>
for (let i = 0; i < 1000000; i++) {
console.log(i)
}
document.querySelector('.box').innerHTML = '程序員Sunday'
而 web worker 就可以解決掉卡頓的問(wèn)題。
圖片
Web Worker 是一種 Web API,它提供了在 后臺(tái)線(xiàn)程 中運(yùn)行 JavaScript 的能力。這使得我們可以將耗時(shí)的計(jì)算任務(wù)移至后臺(tái)線(xiàn)程,避免了主線(xiàn)程被阻塞,從而提升應(yīng)用性能。
想要使用 Web Worker,那么需要先明確 一個(gè)變量、一個(gè)構(gòu)造、兩個(gè)方法:
變量 self
self 變量是 Web Worker 中的全局上下文對(duì)象。它類(lèi)似于瀏覽器中的 window 對(duì)象,但是在 Web Worker 中,window 是不可用的,因?yàn)?Worker 是在一個(gè)獨(dú)立的線(xiàn)程中運(yùn)行的。
self 代表了 Worker 自己的全局作用域,所有在 Worker 內(nèi)部定義的全局變量和方法都掛載在 self 上。例如:
self.onmessage = function(event) {
console.log('收到主線(xiàn)程消息:', event.data);
};
構(gòu)造函數(shù) Worker
創(chuàng)建一個(gè) Web Worker 實(shí)例需要使用 Worker 構(gòu)造函數(shù)。該構(gòu)造函數(shù)接受一個(gè)字符串參數(shù),指向包含 Worker 代碼的 JavaScript 文件的路徑。這意味著 Worker 代碼和主線(xiàn)程代碼是分開(kāi)的,Worker 可以獨(dú)立執(zhí)行:
const worker = new Worker('worker.js');
PS:Web Worker 只能加載與同源策略相同的腳本,因此需要確保 Worker 文件在同一個(gè)域下,或者設(shè)置適當(dāng)?shù)?CORS 頭部來(lái)允許跨域訪(fǎng)問(wèn)。
方法一:onmessage
onmessage 是 Worker 中常用的方法之一。它用于接收來(lái)自主線(xiàn)程通過(guò) postMessage 發(fā)送的數(shù)據(jù)。
當(dāng)主線(xiàn)程調(diào)用 worker.postMessage() 發(fā)送數(shù)據(jù)時(shí),Worker 會(huì)觸發(fā) self.onmessage 事件,并接收數(shù)據(jù)。示例如下:
self.onmessage = function(event) {
console.log('收到來(lái)自主線(xiàn)程的消息:', event.data);
// 處理數(shù)據(jù)并返回給主線(xiàn)程
self.postMessage('計(jì)算完成');
};
方法二 postMessage
postMessage 是 Web Worker 與主線(xiàn)程之間通信的核心方法之一。主線(xiàn)程或 Worker 都可以調(diào)用該方法向?qū)Ψ桨l(fā)送數(shù)據(jù)。它必須接受一個(gè)參數(shù),該參數(shù)將被傳輸?shù)綄?duì)方。在主線(xiàn)程中,postMessage 的調(diào)用如下:
worker.postMessage('開(kāi)始計(jì)算');
在 Worker 中,使用 self.postMessage() 來(lái)發(fā)送結(jié)果或數(shù)據(jù)回主線(xiàn)程:
self.postMessage('結(jié)果數(shù)據(jù)');
以下是一段相對(duì)完整的實(shí)例代碼
// worker.js
self.onmessage = () => {
for (let i = 0; i < 1000000; i++) {
console.log(i)
}
}
// index.html
<script>
const worker = new Worker('./worker.js')
worker.postMessage(1)
document.querySelector('.box').innerHTML = '程序員Sunday'
</script>
Web Worker 的注意事項(xiàng)
對(duì)于 Web Worker 來(lái)說(shuō),我們除了需要知道它的使用方式之外,還有幾個(gè)關(guān)鍵的注意事項(xiàng)需要掌握
1. 無(wú)法操作 DOM
Web Worker 是在獨(dú)立的線(xiàn)程中運(yùn)行的,而不是在主線(xiàn)程中。因此,它無(wú)法直接操作 DOM。
這一點(diǎn)是 Web Worker 的一個(gè)重要限制。由于 Web Worker 與主線(xiàn)程之間的環(huán)境是隔離的,無(wú)法直接訪(fǎng)問(wèn)或修改網(wǎng)頁(yè)的 DOM 元素。這意味著,你無(wú)法通過(guò) Web Worker 來(lái)更新頁(yè)面內(nèi)容、修改樣式或進(jìn)行 UI 交互。
不過(guò),雖然 Web Worker 無(wú)法直接操作 DOM,但我們?nèi)匀豢梢酝ㄟ^(guò) 主線(xiàn)程 和 Web Worker 之間的 消息傳遞機(jī)制 來(lái)實(shí)現(xiàn)間接的 DOM 操作。
Web Worker 可以通過(guò) postMessage 向主線(xiàn)程發(fā)送數(shù)據(jù),然后由主線(xiàn)程接收數(shù)據(jù)并操作 DOM。例如:
// 主線(xiàn)程
const worker = new Worker('worker.js');
worker.onmessage = function(event) {
// 在這里操作 DOM
document.getElementById('result').textContent = event.data;
};
worker.postMessage('開(kāi)始計(jì)算');
// Worker 中的代碼
self.onmessage = function(event) {
// 進(jìn)行一些計(jì)算
const result = '計(jì)算結(jié)果';
postMessage(result); // 結(jié)果傳遞回主線(xiàn)程
};
通過(guò)這種方式,Web Worker 可以處理復(fù)雜的計(jì)算或數(shù)據(jù)處理任務(wù),然后將結(jié)果通過(guò)消息傳遞的方式傳遞回主線(xiàn)程,由主線(xiàn)程來(lái)更新 DOM,保持了線(xiàn)程間的良好分離。
2. 錯(cuò)誤處理(Handle Errors)
Web Worker 是在獨(dú)立的線(xiàn)程中運(yùn)行的,這意味著任何錯(cuò)誤或異常都不會(huì)像在主線(xiàn)程中那樣直接影響用戶(hù)界面。因此,對(duì)于 Web Worker 的錯(cuò)誤處理需要特別注意。
常見(jiàn)的錯(cuò)誤類(lèi)型
Web Worker 中可能遇到的常見(jiàn)錯(cuò)誤包括:
- SyntaxError:Worker 文件中的語(yǔ)法錯(cuò)誤。
- ReferenceError:引用不存在的變量或函數(shù)。
- TypeError:不正確的變量類(lèi)型使用。
- PostMessage 錯(cuò)誤:傳遞給 postMessage 的對(duì)象不符合序列化要求(例如,無(wú)法序列化的對(duì)象)。
如何捕獲 Worker 錯(cuò)誤?
Web Worker 提供了 onerror 事件來(lái)捕獲 Worker 線(xiàn)程中的錯(cuò)誤。你可以為 Worker 實(shí)例設(shè)置錯(cuò)誤處理回調(diào)函數(shù)。例如:
worker.onerror = function(event) {
console.error('Worker 錯(cuò)誤:', event.message, '在', event.filename, '的', event.lineno, '行');
};
這個(gè)錯(cuò)誤處理回調(diào)將捕獲并打印出 Worker 中的錯(cuò)誤信息,包括錯(cuò)誤消息、文件名和出錯(cuò)行號(hào)。
Worker 內(nèi)部的錯(cuò)誤處理
在 Worker 內(nèi)部,你還可以使用 try...catch 來(lái)捕獲異常并將錯(cuò)誤信息傳遞回主線(xiàn)程。例如:
self.onmessage = function(event) {
try {
// 執(zhí)行可能出錯(cuò)的代碼
let result = someFunction(event.data);
postMessage(result);
} catch (error) {
postMessage('錯(cuò)誤:' + error.message); // 傳遞錯(cuò)誤信息回主線(xiàn)程
}
};
這樣可以確保 Worker 在執(zhí)行過(guò)程中出現(xiàn)異常時(shí)不會(huì)導(dǎo)致線(xiàn)程崩潰,而是能夠優(yōu)雅地處理錯(cuò)誤并將信息反饋給主線(xiàn)程。
使用 terminate 和 close
在某些情況下,如果 Web Worker 發(fā)生嚴(yán)重錯(cuò)誤或者不再需要繼續(xù)執(zhí)行,可以使用 terminate() 方法終止 Worker。在 Worker 內(nèi)部,也可以使用 self.close() 來(lái)主動(dòng)關(guān)閉 Worker。這兩者之間的區(qū)別在于,terminate() 會(huì)強(qiáng)制結(jié)束 Worker,且不會(huì)觸發(fā)正常的關(guān)閉事件,而 close() 是在 Worker 自己主動(dòng)結(jié)束時(shí)調(diào)用的。
例如,在 Worker 中:
if (someCriticalError) {
self.close(); // 關(guān)閉 Worker
}
在主線(xiàn)程中:
worker.terminate(); // 強(qiáng)制終止 Worker