Web Worker - 網頁的影分身之術!
David / 2022-08-20
在前端處理複雜計算的時候很容易遇到 Main threrad 被卡住,造成畫面將在那裡的囧境,今天想跟大家分享如何使用 web worker 來解決這個問題。這篇文章不會提到太多 code,主要是在介紹這個技術。
什麼是 Web Worker
Web Workers are a simple means for web content to run scripts in background threads. - MDN
看了蠻多文章後我覺得可以整理成三個重點:
- Web worker 就是一個網頁版本的 mutil-threading 解決方案。
- 他不能存取 window,也不能直接更改 DOM 的元素。
- 他只能透過 postMessage 跟 onMessage 來跟 main thread 做溝通
註:postMessage 跟 onMessage 其實就是 JSON.stringfy 跟 JSON.parse 加上資料傳遞的邏輯。
為什麼要使用 Web Worker
因為 javascript 是 single thread 的關係,在前端做複雜運算的時候會發生畫面被程式碼 block 住的狀況:
前端工程師當然要盡可能地避免任何一切畫面的卡頓,所以就想了個技術來解決 javascript 的線成的問題,這就是 web worker。可以把 web worker 想像成網頁的小弟,網頁可以把一些繁重的事情丟給 web worker 做,這樣網頁就可以去處理更重要的事(像是處理畫面),等 web worker 做完工作把結果報告上來之後,網頁再去對這些運算好的資料做畫面或邏輯處裡。
以下就是套用了 web worker 做一樣的運算的結果:
可以看到 UI 就不會因為要運算就讓畫面卡住,大大提升使用者體驗。
何時要使用 Web Worker
同上一個段落所說的,任何對主畫面會造成卡頓的運算其實都可以丟給 web worker 做處理,或是換個說法,任何適合用 multi-thread 的事情都可以交給 web worker 做。
如何使用 Web Worker
接下來簡單地跟大家分享一下怎麼使用 web worker。
以下範例是在 react 中實際操作的
第一步,我們要先建立 web worker:
worker 可以用 onmessage 來聽 main thread 給他的資料,並用 postMessage 傳回給 main thread
const worker = () => {
onmessage = (message) => {
if (message.data === "calculate") {
let sum = 0;
for (let i = 0; i < 100000000; i++) {
for (let x = 0; x < 10; x++) {
sum += i * x;
}
}
postMessage({ sum });
}
}
}接著在 main thread 上就可以建立一個 worker,其中最關鍵的程式碼就是const worker = new Worker(computeWorker) ,這樣就建立一個新的 worker 了,接下來要做的是綁定他的 eventListener:
這邊是搭配 react useEffet 一起做使用
React.useEffect(() => {
const worker = new Worker(computeWorker);
worker.addEventListener("message", (message) => {
setSum(message.data.sum);
});
return () => {
worker.removeEventListener("message", (message) => {
setSum(message.data.sum);
});
};
}, []);然後再把執行運算的 function 傳給 button,就完成了!
<button
onClick={() => {
setSum("computing");
worker.postMessage("calculate");
}}
>
compute on worker thread
</button>使用 Web Worker 需注意的事
- web worker 並不會讓程式變快,甚至有可能變慢(可以看一下上面 gif 的時間比較),因為必須要把 postMessage 跟 onMessage 的運算時間考慮進去。
- 如同一開始提到的,web worker 不能存取 window, dom,所以如果想要在 worker 裡面改變畫面,他會直接噴錯給你看。
- 支援度:Can I Use ,Global 達到了 98.54 支援度相當好。
Demo Page
最後附上上面截圖的程式碼:CodeSandbox
以上就是今天的分享,感謝大家閱讀。