Web Workersは、Webアプリケーションにおいてバックグラウンドでの処理実行を実現するための技術だ。

HTMLとJavaScriptで作成したプログラムは、基本的にUIスレッド内で行われるため、JavaScriptでの処理に時間がかかるとその分UIの応答性が劣化する。最悪の場合は、ブラウザがフリーズした挙句に「スクリプトが応答しない」というダイアログが表示されてしまい、処理を中断されてしまうこともある。

Web Workersを使用すると、バックグラウンドで動作するスレッド(ワーカ)を簡単に生成することができるため、長時間にわたる処理はワーカに任せることで、UIの応答を妨げることがなくなる。

ワーカを生成するのは非常に簡単だ。Workerクラスのコンストラクタに、ワーカのソースコード(JavaScriptファイル)を指すURLを渡せばよい。

var worker = new Worker("worker.js");

ただし、バックグラウンドワーカはDOMにアクセスできないのには注意が必要だ。そのため、ワーカのソースコード中では、windowやdocumentという変数を使用するとエラーが発生してしまう。

また、ワーカとはメッセージの送受信によってデータのやり取りを実現する。ワーカからメッセージを受信するには、ワーカのonmessageイベントハンドラにコールバック関数を設定する。

worker.onmessage = function(event) {
  // メッセージを処理
};

ワーカに対してメッセージを送信するには、postMessage()メソッドを使用する。メッセージは、任意のJavaScriptオブジェクトとなる。

worker.postMessage(message);

また、ワーカ内でメッセージを受信したり、ワーカから生成元のワーカに対してメッセージを送信するには、上と同じくonmessage、postMessage()を使用する。

では、ワーカを実際に使用したサンプルを見ていくことにしよう。

ワーカを使用したサンプル

以下のサンプルは、テキストフィールドに数字を入力して「計算」ボタンを押すと、1からその数値までの合計をループで算出すると言う単純なものだ。

ワーカのサンプル

このサンプルを単純に実装すると以下のようになる。

ワーカを使用せずに実装したパターン

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
function calculate() {
  var num = parseInt(document.getElementById("num").value, 10);
  var result = 0;
  // ループで合計を計算
  for (var i = 0; i <= num; i++) {
    result += i;
  }
  alert("合計は" + result + "です。");
}
</script>
</head>
<body>
<h1>1から入力値までの合計を算出</h1>
数値を入力:<input type="text" id="num">
<button onclick="calculate()">計算!</button>
</body>
</html>

このように実装すると、合計を算出するためのループ処理が行われている間はUI操作を行うことができない。テキストフィールドに入力する数値が1,000や1万といった小さな数値である間はそれでも問題ないが、1億、10億などを指定するとブラウザが計算中フリーズしてしまう(筆者のマシンでは、100億を指定したところでFirefoxが完全にフリーズし、強制終了せざるを得なかった)。

上のサンプルを、ワーカを使用して書き直したのが以下のコードだ。こちらのバージョンでは、どれほど巨大な数値を指定してもブラウザがフリーズすることはなく、バックグラウンドで処理が行われる。ユーザビリティの違いは明白だ。

HTMLファイルでは、入力された文字列を数値に変換してワーカに渡す処理と、ワーカから計算結果を受け取ってアラート表示する処理を行っている。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
// 計算を行うワーカを生成
var worker = new Worker("worker1.js");
// ワーカから結果を受け取る
worker.onmessage = function(event) {
  // dataプロパティにメッセージ本文(任意のJSオブジェクト)が格納されている
  alert("合計は" + event.data + "です。");
};
function calculate() {
  var num = parseInt(document.getElementById("num").value, 10);
  // ワーカに数値を渡す
  worker.postMessage(num);
}
</script>
</head>
<body>
<h1>1から入力値までの合計を算出</h1>
数値を入力:<input type="text" id="num">
<button onclick="calculate()">計算!</button>
</body>
</html>

以下はワーカのソースコード(worker.js)だ。イベントオブジェクトのdataプロパティにアクセスして、ユーザが入力した数値を取得し、ループ処理によって合計値を求めている。

// メッセージの受信
onmessage = function(event) {
    var num = event.data;
    var result = 0;
    for (var i = 0; i <= num; i++) {
    result += i;
    }
    // 生成元に結果を返す
    postMessage(result);
}

このサンプルは、Firefox3.5、Safari4、Chrome3で動作する(Operaでは動作しない)。

このようにワーカのソースコード内では、onmessageやpostMessage()と言った特殊なグローバル関数を利用可能だ。ほかにも、以下のような変数や関数、クラスをワーカ内で使用することができる。

  • self … ワーカのグローバルスコープ
  • postMessage(message) … メッセージをワーカの生成元に送信する
  • onmessage … メッセージを受信した際に呼ばれるイベントハンドラ
  • importScripts(urls) … 他のJavaScriptファイルを読み込む。引数は可変長で複数指定可能
  • navigator … window.navigatorと同様に、appName、platform、userAgent、appVersionといったプロパティを持つ
  • openDatabase()/openDatabaseSync() … Web Databaseをワーカからも利用できる
  • sessionStorage/localStorage
  • XMLHttpRequest
  • Worker
  • setTimeout()/setInterval()などのタイマー
  • WebSocket/EventSource(Server-Sent Events)/MessageChannelなど(実装しているブラウザはまだない)