JavaScript は基本シングルスレッド(UIスレッド)でイベントループを回しつつ処理を実行してます。UIスレッドで重い処理を行うと一時的に画面がフリーズしたり、画面が表示されるまでの速度が遅くなったりの原因になります。
Promise
やsetTimeout
などとの違いは、それらは非同期で実行されますが UIスレッド上で処理されます。Web Worker も非同期ですが別のスレッドで処理されるという違いがあります。
仕組み
Web Worker はイベントで UIスレッドとのデータのやり取りを行います。どちらのスレッドでも使うのは2つのメソッドだけです。
#addEventListener('message', () => { /* ... */ }
によりイベントハンドラーを登録し、送られてきたデータを処理#postMessage(data)
でデータを別スレッドへ送信
UI Other
+------------+ +------------------------------------+
| ping! | +-----------> |addEventListener('message', handler)|
+------------+ +------------------+-----------------+
|
|
|
|
|
v
+--------------------------------------------+ +-------+-----+
|addEventListenerHandleer('message', handler)| <------+ | pong! |
+--------------------------------------------+ +-------------+
制限
Web Worker が動くスレッドでは以下のような制限が掛かります。
window
document
あらゆる DOM 要素
Web Worker ファイルを作る
Worker ファイルは拡張子を.worker.js
とするケースが多いようなのでこのルールに則りworkers/ping-pong.worker.js
というファイルを作ります。このファイルの中にはself
というDedicatedWorkerGlobalScope
がグローバル領域で使えるようになっており、これを通してイベントを扱ったり、データを送信したりします。
以下のコードは送られてきたデータのtype
プロパティ値がping
だった場合pong
という文字列データを返す Web Worker です
self.addEventListener('message', event => {
if (event.data.type === 'ping') {
self.postMessage('pong');
}
});
Worker インスタンス
スレッドは UI スレッドから Worker コンストラクタを通して建てれます。その時 Worker へは Web Worker となる.js
ファイルへのパスを渡します。先程のファイルを使うと以下のようになります。
const worker = new Worker('workers/ping-pong.worker.js');
UI スレッド側でも同じ用にイベントを扱うようにしたり、データを送信します。基本図のように UI スレッドから何かしら処理の起点をあげなければ何も始まりません。
worker.addEventListener('message', event => {
console.log(event.data);
});
worker.postMessage({type: 'ping'});
もしコードが即実行されるようになっていればリロードする度にpong
と表示されるはずです。
また上記のような感じで実装した例です。この例はクリックしたら…ですがその度pong
とコンソールに表示されます。