Javascriptはシングルスレッドなため、重い処理を実行するとUIの描画処理がブロックされてしまうが、
WebWorkerを使うことでこの問題を解決することができる。
目次
WebWorkerとは
ここに書いてある通り
https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API/Using_web_workers
WebWorker は、ウェブコンテンツがスクリプトをバックグラウンドのスレッドで実行するためのシンプルな手段です。 Worker スレッドは、ユーザーインターフェイスを妨げることなくタスクを実行できます。
つまりWebWorkerを用いることでマルチスレッドで並列処理を実現することができます。
WebWorkerの使い方
Workerオブジェクトの生成
1 |
const worker = new Worker('worker.js'); |
WebWorkerのパスを指定してWorkerコンストラクタを呼び出すだけ
メインスレッドとWebWorkerの送受信
メッセージシステムを使用してメインスレッドとWebWorkerでデータのやり取りをします
メインスレッドからWebWorkerに送信する
1 |
worker.postMessage('10antz'); |
WebWorkerで受信する
1 2 3 |
self.addEventListener('message', function(message) { console.log(message.data); // 10antz }); |
messageイベントListenします
WebWorkerからメインスレッドに送信する
1 |
self.postMessage('10antz'); |
メインスレッドで受信する
1 2 3 |
worker.addEventListener('message', function(message) { console.log(message.data); // 10antz }); |
メインスレッドもWorkerもpostMessageとmessageイベントを使用してメッセージを送受信します
具体的にWebWorkerの効果を試してみます
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 33 |
<body> <button id="button">without worker</button> <button id="button2">with worker</button> <p>Timer:<span id="time">0</span></p> <p>Count:<span id="count">0</span></p> <script> let time = 0; let timer = setInterval(()=> { time++; $('#time').text(time); if(time === 100) { clearInterval(timer); } }, 100); $('#button').click(e => { const time = 1000; let start = Date.now(), count = 0; while (Date.now() - start < time) count++; $('#count').text(count); }); $('#button2').click(e => { const worker = new Worker('worker.js'); worker.addEventListener('message', function(e) { $('#count').text(e.data); }, false); worker.postMessage(1000); }); </script> </body> |
1 2 3 4 5 6 |
self.addEventListener('message', function(e) { let start = Date.now(), count = 0; while (Date.now() - start < e.data) count++; self.postMessage(count); }, false); |
0.1秒の間隔で描画し続けるタイマーと
1秒間のループ数をカウントし描画するボタンがあります
(WorkerではDOMにアクセスできないのでメインスレッドで結果を受け取ってからDOMを更新する必要があります)
メインスレッドで実行
メインスレッドで実行すると結果が描画されるまではタイマーの描画が止まってしまいます
WebWorkerで実行
Workerで実行するとタイマーの描画は止まらずに結果を描画することができます
このように重い処理をWorkerで実行することで、メインスレッドの処理を妨げず、パフォーマンスを向上させることができます
WorkerをESモジュールのように扱う
ESモジュールを利用してる環境では、WebWorkerをモジュールのように扱うことのできるloaderがあります
https://github.com/developit/workerize-loader
Workerでは関数をexportするだけ
1 2 3 4 5 6 |
export function counter(time) { let start = Date.now(), count = 0; while (Date.now() - start < time) count++; return count; } |
メインスレッドではWorkerをインポートして
1 |
import worker from 'workerize-loader!./worker' |
インスタンスを生成してexportした関数を実行するだけ
1 2 3 4 5 6 |
$('#button').click(e => { let instance = worker(); instance.counter(1000).then( count => { $('#count').text(count); }) }); |
メッセージのやり取りが簡単な上、async/awaitも動作するのでおすすめです