iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 19
2
Modern Web

用 Javascript 當個影像魔術師系列 第 19

Day 19 - Canvas 效能調整 - Web Worker

Day 19 - Canvas 效能調整 - WebWorker

效能調整

進行到這邊之後,目前依照我的電腦情況,在進行某些濾鏡操作時,會發現畫面會有明顯的卡頓,無法維持在順暢的更新率,從飽和度開始每次處理完的時間就已經超過維持 60 fps 的更新頻率 ( 16.67 ms ),進而造成主線程的卡頓,這個時候任何跟 DOM 的交互都不會馬上有立即反應,對於使用者體驗來說會非常差。為了更凸顯這個現象,寫一個簡單的費式數列計算來看實際卡頓的情形。

可以看到在主線程在做大量計算的時候,整個都被卡住,直到最終結果出現之後才開始恢復正常。

畫面更新

圖片來自

在每一次的更新裡面,瀏覽器的主線程都會經過好幾個階段才會將最後的畫面呈現出來,所以在這些階段裡面,如果有哪一個階段耗費的時間特別長,就有可能會導致更新頻率不佳,以我們這個例子就是在執行 Javascript 的時候耗費太久時間。所以現在我們有兩個目標

  1. 將複雜的計算任務移到其他線程去處理,讓主線程一直保持著良好的互動狀態
  2. 盡可能想辦法縮短 Javascript 執行時間

那就從第一個開始吧!

web Worker

MDN參考連結

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 有幾個比較大的限制,在使用上要注意

  1. 線程間因為資源不共享,要傳遞資料需靠 message 的方式傳遞,但要注意的是在傳遞過程中會經歷序列化傳輸,然後在另一端反序列化取出 ( 就是資料兩邊都會複製,不會共享 ),如果資料很大的話開銷也會增大,除了有實作 Transferable 介面可以直接傳遞之外,其他時候使用上請小心
  2. 無法使用大部分的 API 及獲取 DOM能夠使用的功能

小結

今天介紹到了 worker ,讓原本在主線程會阻塞的計算操作可以移到裡面去進行,在實際應用上計算密集的像是圖像、音頻需要大量計算的都有機會用到,帶給使用者更好的體驗,明天見!


上一篇
Day 18 - Canvas 影片彈幕
下一篇
Day 20 - Canvas 效能調整 - OffscreenCanvas 及 ImageBitmap (上)
系列文
用 Javascript 當個影像魔術師30

尚未有邦友留言

立即登入留言