進行到這邊之後,目前依照我的電腦情況,在進行某些濾鏡操作時,會發現畫面會有明顯的卡頓,無法維持在順暢的更新率,從飽和度開始每次處理完的時間就已經超過維持 60 fps 的更新頻率 ( 16.67 ms ),進而造成主線程的卡頓,這個時候任何跟 DOM
的交互都不會馬上有立即反應,對於使用者體驗來說會非常差。為了更凸顯這個現象,寫一個簡單的費式數列計算來看實際卡頓的情形。
可以看到在主線程在做大量計算的時候,整個都被卡住,直到最終結果出現之後才開始恢復正常。
在每一次的更新裡面,瀏覽器的主線程都會經過好幾個階段才會將最後的畫面呈現出來,所以在這些階段裡面,如果有哪一個階段耗費的時間特別長,就有可能會導致更新頻率不佳,以我們這個例子就是在執行 Javascript
的時候耗費太久時間。所以現在我們有兩個目標
Javascript
執行時間那就從第一個開始吧!
Web Workers 可以讓網頁在背景執行中執行程式,而不干擾主線程,也就是說我們可以把上列需要高度計算的程式移動到裡面,也因為如此,所以在主線程原有的更新就不會受到影響,那接下來就看如何實作吧!
Webpack 可能要設定一下 worker-plugin
我們先建立一個新的 js
檔
import { fib } from './utils'
onmessage = e => {
const result = fib(e.data.fibCount)
postMessage(result)
}
因為 worker
跟主線程並不是在同一個環境裡,所以並沒有能力去取得主線程裡面的資源如變數,所以溝通的方式就必須透過接收方法 onmessage
以及傳送方法 postMessage
來互相溝通,所以在這邊我們定義 worker
在接收到訊息的時候做費式數列的運算,並且將結果回傳給主線程。
接著在主要檔案裡使用
// 初始化 worker
const worker = new Worker('../../lib/canvas.worker.js', { type: 'module' })
// 在觸發按鈕時,傳送訊息給 worker 做計算
worker.postMessage({ type: 'fib', fibCount: 40 })
// 定義在接受到 worker 訊息後更新算完結果
worker.onmessage = (e) => {
this.fibResult = e.data
}
主要就是使用 new Worker
來新增,並且定義好接受跟傳的值,跟我們剛剛在 worker
裡面做的值一樣,如果一切順利的話,這一段計算流程將會給 worker
執行,就不會堵塞我們的畫面更新了。結果如下,可以看到就算在計算過程中,主線程的 UI
也不會受影響
可以看到影片在計算中還是會自動更新,等到結果回來之後會直接顯示,主線程跟 worker
的溝通形式很像 websocket
,所以在之後有需要大量運算的東西可以考量交給 worker
來做,避免讓畫面更新卡頓,但要注意一點的是 worker
有幾個比較大的限制,在使用上要注意
message
的方式傳遞,但要注意的是在傳遞過程中會經歷序列化傳輸,然後在另一端反序列化取出 ( 就是資料兩邊都會複製,不會共享 ),如果資料很大的話開銷也會增大,除了有實作 Transferable 介面可以直接傳遞之外,其他時候使用上請小心API
及獲取 DOM
,能夠使用的功能
今天介紹到了 worker
,讓原本在主線程會阻塞的計算操作可以移到裡面去進行,在實際應用上計算密集的像是圖像、音頻需要大量計算的都有機會用到,帶給使用者更好的體驗,明天見!