JavaScriptコードの重い処理を、バックグラウンドで動作させてレスポンスを改善したい。それなら、Web Workersを使おう。
powered by Insider.NET
HTML 4が登場したころは、シングルコアのCPUしかなく、HTMLとJavaScriptで実現できる処理も限定的であったため、処理を逐次実施するシングルスレッドでの処理に不満を抱くことは少なかった。もっとも、HTMLとJavaScriptは同一のUIスレッド上で処理を行うため、JavaScriptで動的にHTMLコードを生成する処理や、高負荷の処理がある場合、HTMLレンダリングが遅延したり、最悪の場合はブラウザがフリーズしたりしてしまうということがあった。
近年では、マルチコアの端末が大多数を占めるという物理的な環境の改善のほか、HTML5の台頭により、HTMLとJavaScriptを高度に活用したWebアプリケーションが増えてきている。そのため、JavaScriptコード自体をマルチスレッドで処理したいという要件も多くなりつつある。
例えばファイルにアクセスする際のI/Oや高負荷な計算の処理などは、依然として時間がかかりやすい処理だ。このような時間のかかる処理に対してUIスレッドではなく、バックグラウンドのスレッドでJavaScriptコードの処理を実施したいというニーズもある(バックグラウンドで動作するJavaScriptファイルのことを「ワーカー」という)。
そこで「Web Workers」の登場だ。今回紹介するWeb Workers(=ワーカー)を利用することで、JavaScriptコードの処理を並行して実行できるので、HTML5を活用したWebアプリケーションのレスポンスを改善できる。
ただし、ワーカーはその特性から利用できるシナリオが限られている。ざっと列挙すると、以下のシナリオが考えられるだろう(詳しくは後述する)。
ワーカーのイメージは図1のようになる。
今回は1〜2のシナリオについて解説を進める。
最初に各ブラウザの実装状況だが、Web Workersは次の表のとおりで、現在、リリースされている主要ブラウザでは、ほとんど実装済みだ。
ブラウザ | 対応バージョン |
---|---|
Internet Explorer | 未実装 |
Firefox | 3.5以降 |
Chrome | 3以降 |
Safari | 4以降 |
Opera | 10.6以降 |
主要ブラウザにおけるWeb Workersの実装状況 |
唯一実装されていないIEでは、次期バージョンとなる10からWeb Workersが正式対応の予定だ。
さっそくワーカーの解説に進みたいと思うが、その前にワーカーの実装の有無を確認するコードを紹介する。ワーカーの実装の有無は、以下のコードで確認できる。
if (window.Worker){
// ワーカーに関する処理を記述
} else {
window.alert("本ブラウザではWeb Workersが使えません");
}
windowオブジェクトのWorkerプロパティがある場合はワーカーが利用できるものとして実処理を、ない場合は利用できないエラー・メッセージの表示などを行うのが一般的だ。
では、順に使用方法を見てみよう。
最初にワーカーの恩恵を感じてもらうために、高負荷な計算処理を例にシングルスレッド時とワーカー利用時の違いを見てみよう。
単純ではあるが、倍数を求めるアプリケーションを試してほしい。「1〜<1つ目のテキストボックスで指定された数値>までの間に、<2つ目のテキストボックス>の倍数がいくつあるか」を求めている。実行例は図2のとおりだ。こちらから実際に試せる。
このサンプル・アプリケーションの実際のコードは、以下のとおりだ。
割り算元の数値:<input type="text" id="inputtotalnum" /><br/>
割る数値 :<input type="text" id="num" />
<input type="button" value="送信" onClick="send()">
<br />
<div id="disp" ></div>
<script type="text/javascript">
function send() {
var total =document.getElementById("inputtotalnum").value;
var num = document.getElementById("num").value;
var i = 0
var n = 0;
// 倍数計算
for (i = 1; i < total; i++) {
if (i % num == 0) n++;
}
document.getElementById("disp").innerHTML =total+"における"+ num +"の倍数は" + n + "個です";
}
</script>
単純に倍数の個数をカウントしているだけのコードだが、入力された数値次第ではこれだけのコードでも図2のように、ブラウザが応答しなくなる(筆者の環境では[割り算元の数値]テキストボックスに「100000000」を、[割る数値]テキストボックスに「2」を指定することで再現された)。
それでは、今度はワーカーを使用した例を見てみよう。実行結果は図3のようになる。桁数を上げても、処理に時間こそかかるものの、ブラウザは応答可能な状態で処理を完遂できるようになる。このサンプルは、こちらから実際に試せる。
それでは、ワーカーをどのように使用したのか、確認してみよう。まずはUIスレッド側のコードだ。上のコードから差分だけ記載する。
<script type="text/javascript">
// (1)ワーカーの実装チェック
if (window.Worker) {
// Web Workersに関する処理を記述
document.getElementById("button").addEventListener("click", send, false);
var worker = new Worker("multiple.js");
} else {
window.alert("このブラウザではWeb Workersは利用できません");
}
function send() {
var total =document.getElementById("inputtotalnum").value;
var num = document.getElementById("num").value;
// (2)ワーカーに入力値を送る
worker.postMessage({ "total": total, "num": num });
}
// (3)ワーカーからのメッセージ取得時の処理
worker.onmessage = function (event) {
document.getElementById("disp").innerHTML = event.data;
}
</script>
(1)ワーカーの実装チェック
最初に「ワーカーを実装しているか」をチェックし、実装している場合は、倍数計算を実施しているワーカーを作成している。ワーカーを作成するには、Workerオブジェクトを生成するコンストラクタの引数に対して、実際のロジックを表すJavaScriptファイル(ここでは「multiple.js」)を指定するだけだ。
(2)ワーカーに入力値を送る
ワーカーはメッセージのやりとりが基本となる。使用できるイベント・ハンドラとメソッドは次のとおりだ。
名前 | 概要 |
---|---|
onmessage | ワーカー⇔UIスレッド間でメッセージ取得時のイベント・ハンドラ |
onerror | エラー発生時のイベント・ハンドラ |
postMessage | ワーカー⇔UIスレッド間でメッセージを送信 |
terminate() | ワーカーを停止 |
Workerオブジェクトのイベント・ハンドラとメソッド |
sendメソッド内で、ワーカーに対して、入力値をメッセージとして送るpostMessageメソッドを使用している。その引数には、任意のオブジェクトを指定できる。今回は入力された値を同名のプロパティにセットしたうえで、そのオブジェクトをワーカーに送っている。
(3)ワーカーからのメッセージ取得時の処理
ワーカーからメッセージを取得したときの処理をonmessageメソッドに記載している。引数となるeventに格納されたデータは、MessageEventインターフェイスを通して受け取る。例えば受信したメッセージ・データは、dataプロパティを参照して取得できる。
今回は、ワーカーから取得したメッセージのデータをそのまま表示している。
続いて、ワーカー側で使用されるJavaScriptファイルのコードだ。
onmessage = function (event) {
var i = 0
var n = 0;
var ret = event.data;
// 倍数計算
for (i = 1; i < ret.total; i++) {
if (i % ret.num == 0) n++;
}
// UIスレッド側にメッセージを送付する
postMessage(ret.total + "における" + ret.num + "の倍数は" + n + "個です");
}
ワーカーでは、実処理をonmessageプロパティとして指定する必要があるが、そのほかは、もともとシングルスレッドで指定していた処理をそのまま記載しているだけだ。UIスレッドからのデータはevent.dataプロパティで取得できる。
UIスレッド側にメッセージを返送するには、postMessageメソッドを呼び出せばよい。
以上の流れを見て気付く読者諸氏も多いと思うが、基本的にワーカーはシングルスレッドで動作していたものを、ほぼそのままの形で分離できるわけだ。ただし、全ての処理をワーカーで実装することはできない。ここで、ワーカーの留意点に触れてみよう。
●利用できる機能やオブジェクトの制限
ワーカーは、メインのUIスレッドとは別のスレッドで動作するので、直接にUI(DOM)を操作することはできない。つまり、CSSセレクタなどでアクセスすることが前提となるjQueryや、そのほかのDOM操作ありきのライブラリは、ワーカー上では動作しない可能性がある。ワーカー上で利用できる機能やオブジェクトの可否は、以下のとおりだ。
オブジェクトまたは機能名 | 可否 |
---|---|
navigatorオブジェクト | ○ |
locationオブジェクト | ○(読み取りのみ) |
XMLHttpRequest | ○ |
JavaScriptファイルのインポート | ○ |
windowオブジェクト | × |
documentオブジェクト | × |
parentオブジェクト | × |
DOM操作全般 | × |
ワーカー上で利用できる機能やオブジェクトの可否 |
●JavaScriptファイルのインポート
逆に、ワーカー内でのみ利用できる機能として、JavaScriptファイルのインポート機能がある。
importScripts("ファイル名1", "ファイル名2"...);
インポート機能を利用するには、importScriptsメソッドの引数にJavaScriptファイル名をカンマ区切りで記載するだけだ。インポート機能を利用することで、再利用するようなビジネス・ロジックを高度に部品化することが可能になる。
それでは次のページで、実際にワーカーを活用したサンプルを紹介する。
Copyright© Digital Advantage Corp. All Rights Reserved.