JavaScriptで時間のかかる処理などをバックグランドで並列実行できる「Web Worker」を使ってみます。
Web Workerについて
Web Workersの対応ブラウザはこちら
Web Workerには呼び出し元のスクリプトのみアクセスできる「Dedicated Worker」と、複数のスクリプトからアクセスできる「Shared Worker」があります。
実際に使う際の注意点として、WorkerからDOMにアクセスできないため、windowやdocumentにもアクセスできません。
Dedicated Worker
Dedicated Workerを簡単に使ってみます。
上記で記述した通り、Dedicated Workerは呼び出し元のスクリプトのみがアクセスでき、以下のサンプルだとsample.jsが呼び出し元になります。
sample.js
// Workerの作成
var worker = new Worker('worker.js');
// worker.jsに値を送信
worker.postMessage('アンジェ');
// worker.jsから送信された値をconsoleに表示
worker.onmessage = function(event) {
console.log(event.data);
}
まずはnew Worker()でWorkerを生成します。
Workerとのやり取りは、postMessage()メソッドで値の送信、onmessageイベントで値の受信をすることで行います。
次に生成したWorkerで行う処理です。
worker.js
// sample.jsから送信された値を取得
onmessage = function(event) {
var result = event.data + 'さん こんにちは';
// sample.jsに値を送信
postMessage(result);
}
onmessageイベントでsample.jsから送信された値を取得して、postMessage()メソッドを使ってsample.jsに返しています。
Dedicated Workerのデモページ
次にエラーが合った場合の処理を追加してみます。
sample.js
// Workerの作成
var worker = new Worker('worker.js');
// worker.jsに値を送信
worker.postMessage('アンジェ');
// worker.jsから送信された値をconsoleに表示
worker.onmessage = function(event) {
console.log(event.data)
}
// worker.jsを実行時にエラーが発生した場合
worker.onerror = function(event) {
console.log(event);
// Workerを終了する
worker.terminate();
}
12~17行目がエラーがあった場合の処理で、onerrorイベントでエラーを取得して、terminate()メソッドでWorkerの処理を終了させています。
実際にworker.js側でエラーが出るようにしてみます。
worker.js
// sample.jsから送信された値を取得
onmessage = function(event) {
var result = event.data + 'さん こんにちは';
// sample.jsに値を送信
postMessage(a); // ここでエラー
}
consoleにエラーログが表示され、その時点でWorkerの処理が終了するようになりました。
Dedicated Workerのエラー処理のデモページ
Shared Worker
Dedicated Workerは呼び出し元のスクリプトのみアクセスのアクセスでしたが、Shared Workerは別タブやiframeなど別のスクリプトからアクセスできます。
複数のスクリプトでカウントを共有してみます。
index.html
<button id="btn">カウントボタン</button> <div>カウント: <span id="count"></span></div> <iframe src="iframe.html" width="300" height="300"></iframe>
ボタンをクリックするとカウントが追加され、#countに反映されます。
複数のスクリプトからのアクセスを確認するため、iframe内で同じような処理を実装します。
index.htmlで使用するJavaScriptは以下のようにします。
sample.js
// SharedWorkerの作成
var worker = new SharedWorker('worker.js');
// worker.jsから送信された値を出力
worker.port.onmessage = function(event) {
document.getElementById('count').innerHTML = event.data;
}
document.getElementById('btn').addEventListener('click', function() {
// worker.jsに値を送信
worker.port.postMessage('test');
}, false);
Shared Workerの場合、new SharedWorker()でWorkerを生成します。
Workerとのやり取りはDedicated Workerと似ていますが、portオブジェクトを通して通信する点が異なります。
3~6行目でWorkerから値を受け取り#countに反映、8~11行目でボタンクリック時にWorkerに値を送信しています。
次にWorkerでの処理です。
worker.js
var count = 0;
onconnect = function(event) {
var port = event.ports[0];
// sample.jsまたはiframe.jsから送信された値を取得
port.onmessage = function(event) {
count++;
// 送信元に値を送信
port.postMessage(count);
}
}
onconnectイベントはポートへの接続が発生したときに実行されます。
5~10行目でsample.jsまたは後述のiframe.jsから値が送信された時にカウントを1追加して、カウント後の値を送信元に返します。
最後にiframe内のHTMLとJavaScriptです。
iframe.html
<button id="btn">カウントボタン</button> <div>カウント: <span id="count"></span></div>
iframe.js
// SharedWorkerの作成
var worker = new SharedWorker('worker.js');
// worker.jsから送信された値を出力
worker.port.onmessage = function(event) {
document.getElementById('count').innerHTML = event.data;
}
document.getElementById('btn').addEventListener('click', function() {
// worker.jsに値を送信
worker.port.postMessage('test');
}, false);
内容的にはiframeの親と同じです。
これで実際に各カウントボタンをクリックして値が共有されるか試してみます。
Shared Workerのデモページ
iframe内外や別タブで開いていても、Worker内のcountの値は共有されているのが確認できました。
【参考サイト】
コメントが承認されるまで時間がかかります。