Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Web workerを使ってUXを向上させようとした話

kurisaki kazuma
September 26, 2023
300

Web workerを使ってUXを向上させようとした話

We Are JavaScripters! @42nd で発表した資料です。

WeJs
https://wajs.connpass.com/event/293440/

kurisaki kazuma

September 26, 2023
Tweet

Transcript

  1. そこで Web worker を使う Web worker とは メインスレッドとは独立して JS をバックグラウンドで実行できる

    Web API メインスレッドのパフォーマンスに影響を与えず、時間のかかる処理ができる。 メインスレッドとは別なので UI をロックしない 6
  2. Worker の使い方 worker を呼び出す側 worker = new Worker("worker.js"); // worker

    から結果を受け取る worker.onmessage = function (event) { console.log("Received: ", event.data); }; worker.postMessage(10); // worker に 10 を引数で渡して実行 worker.js self.addEventListener("message", (e) => { const result = e.data * e.data; postMessage(result); }); 7
  3. 作成した Worker const constructImageFromBlueprint = (blueprint: Array<Array<string>>) => { //

    設計図からドット画像を構築する処理 ... return result; }; self.addEventListener("message", (e) => { const result = constructImageFromBlueprint(e.data.blueprint); self.postMessage(result); }); 9
  4. カスタムフックで worker の状態を管理 export function useWorker() { const [loading, setLoading]

    = useState(true); const run = ( ) => { setLoading(true); const worker = new Worker( new URL("./worker", import.meta.url) ); worker.onmessage = function (e) { // worker 処理完了 setLoading(false); ... }; worker.postMessage({ blueprint, blockImageDataDict }); }; return { loading, run }; } 10
  5. worker 側から進捗を伝える worker とメインスレッドの共有方法は基本的には message だけ 進捗も結果と同様 message として送る type

    プロパティで進捗地なのか結果なのかを判定 const constructImageFromBlueprint = (blueprint: Array<Array<string>>) => { for (let i = 0; i < rows; i++) { if (i % step === 0) { progress += stepNum; self.postMessage({ type: "loading", payload: progress, }); } ... } self.postMessage({ type: "complete", payload: result, }); }; 12
  6. カスタムフックも進捗を受け取るように修正 export function useImageGeneratorWorker() { const [progress, setProgress] = useState(0);

    // 進捗 const [loading, setLoading] = useState(true); const run = (blueprint: string[][]) => { setLoading(true); const worker = new Worker( new URL("../worker/imageGeneratorWorker", import.meta.url) ); worker.onmessage = function (e) { if (e.data.type == "loading") { setProgress(e.data.payload); // <- 進捗を受け取る return; } setLoading(false); }; worker.postMessage({ blueprint, blockImageDataDict }); }; return { progress, loading, run }; // <- 進捗も返す } 13
  7. requestAnimationFrame との比較 同じようなことは requestAnimationFrame をつかってもできる。 worker マルチスレッドなので環境によってはパフォーマンスがいい worker ファイルを作成して message

    でメインスレッドとやり取り message のやり取りはコピーなので大きいデータのやり取りでパフォーマンスが落ちる 可能性がある reauestnimationFrame シングルスレッドですべての処理を行う 重い処理を関数に分割して再帰的に実行 15
  8. 17