近期在工作上遇到了在網頁進行複雜運算而導致 UI 畫面卡頓的議題,便開始著手研究 web worker,由於 javascript 是單執行緒,一次僅能處理一件事情,有了 web worker 就能夠在背景執行緒處理其他的運算,在執行複雜運算的時候,使用 web worker 可有效避免畫面凍結、卡頓,讓網頁在執行大量運算時使用者依舊可以流暢地操作網頁的介面,但要特別留意的是 web worker 無法直接操作 DOM 元素
web worker 使用流程
建立 web worker
主執行緒使用 postMessage 來和 web worker 溝通
web worker 使用 onMessage 來接收事件
web worker 收到來自主執行緒的請求,執行複雜運算
web worker 運算完畢後,將結果回傳給主執行緒
web worker 是 javaScript 原生提供的方法,不需額外安裝套件,也不侷限於任何一種前端框架使用,各家瀏覽器支援度非常的高!
動手實作 畫面上會有三個按鈕,第一個按鈕 start 會觸發複雜的運算
test1 和 test2 按鈕則是用來模擬網頁頁面上的其他可互動的 ui 元件,這兩個按鈕會在點選的時候,console log 當前的 timeStamp
先建立好 worker.js,裡面有一個模擬複雜運算的函式,當接受到外部傳來的訊息 action 為 calc 就開始執行複雜運算,運算完成之後再將結果用 postMessage 的方式傳遞給主執行緒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const complexCalculation = ( ) => { let result = 0 ; for (let i = 0 ; i < 10000000000 ; i++) { result += i; } return result; }; addEventListener ("message" , (event: MessageEvent ) => { console .log ("event" , event); if (event.data .action === "calc" ) { const result = complexCalculation (); self.postMessage (result); } });
然後在 html 檔案使用 web worker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <body > <button id ="start" > start</button > <button id ="test1" > test1</button > <button id ="test2" > test2</button > <h1 > </h1 > </body > <script > document .getElementById ("start" ).addEventListener ("click" , () => { const worker = new Worker ("./worker.js" ); worker.onmessage = (res ) => { document .getElementsByTagName ( "h1" )[0 ].textContent = `result is ${res.data} ` ; }; worker.postMessage ({ action : "calc" }); }); </script >
按下 start 之後,開始進行複雜的運算,此時點選 test1 和 test2 的按鈕會即時 console log 當下的 timeStamp,意謂著使用者依然可以正常的操作介面 以下為對照組,不建立 web worker,按下 start 直接執行複雜的運算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <body > <button id ="start" > start</button > <button id ="test1" > test1</button > <button id ="test2" > test2</button > <h1 > </h1 > </body > <script > const complexCalculation = ( ) => { let result = 0 ; for (let i = 0 ; i < 10000000000 ; i++) { result += i; } return result; }; document .getElementById ("start" ).addEventListener ("click" , () => { const res = complexCalculation (); document .getElementsByTagName ("h1" )[0 ].textContent = `result is ${res} ` ; }); </script > <script > document .getElementById ("test1" ).addEventListener ("click" , () => { console .log ("test1" + Date .now ()); }); document .getElementById ("test2" ).addEventListener ("click" , () => { console .log ("test2" + Date .now ()); }); </script >
按下 start 後,再去按其他兩個按鈕會完全沒有反應,必須等到計算完成之後,才會 console log 剛剛的操作結果,這對使用者來說就會是一個相當糟糕的操作體驗
結語 現代對於網頁的使用者體驗越來越重視,除了介面易用,更重要的是能不能夠流暢的操作,所以當遇到複雜運算而導致網頁卡頓的問題時,不妨試著使用 web worker 來做優化吧!