並列処理を実現!Web Workersを使いこなそう:連載:人気順に説明する初めてのHTML5開発(2/2 ページ)
JavaScriptコードの重い処理を、バックグラウンドで動作させてレスポンスを改善したい。それなら、Web Workersを使おう。
■ワーカーと同期APIの組み合わせ例
それでは、実際にワーカーを活用したサンプルを紹介する。
前回予告したように、今回は同期APIを利用したファイル内容の取得について紹介する。利用するAPIは、Web Workerと、Drag And Drop API、File APIである。
内容は、前回の記事のサンプルを、ワーカーを利用して実装しただけだ。ドラッグ&ドロップでファイルをドロップした場合に、バックグラウンドのワーカーでテキストファイルか画像ファイルを(同期処理用の)FileReaderSyncオブジェクトで読み取り、その内容をUIスレッドで表示するサンプルを作成する。実行結果は図4〜6になる。こちらから実際に試せる。
まずはUIスレッド側のコードだ。
<div id="drop"
style="width:700px; height:150px; padding:10px; border:3px solid"
ondragover="onDragOver(event)">
ここにドロップしたファイルの内容を表示します。</div>
<br />
<div id="disp" ></div>
<script type="text/javascript">
if (window.Worker) {
// Web Workersに関する処理を記述
var worker = new Worker("fileworkers.js");
if (window.File) {
// File APIに関する処理を記述
document.getElementById("drop").addEventListener("drop", onDrop, false);
} else {
window.alert("本ブラウザではFile APIが使えません");
}
} else {
window.alert("このブラウザではWeb Workersは利用できません");
}
// (1)Drop領域にドロップした際のファイルの読み取り処理
function onDrop(event) {
var files = event.dataTransfer.files;
document.getElementById("disp").innerHTML = "";
for (var i = 0; i < files.length; i++) {
var f = files[i];
worker.postMessage({ "file": f, "type": f.type });
}
// ブラウザ上でファイルを展開する挙動を抑止
event.preventDefault();
}
function onDragOver(event){
// ブラウザ上でファイルを展開する挙動を抑止
event.preventDefault();
}
// (2)ワーカーからのメッセージ取得時の処理
worker.onmessage = function (event) {
var ret = event.data;
if (ret.type.match('image.*')) {
var li = document.createElement('li');
var img = document.createElement('img');
img.src = ret.val;
li.appendChild(img);
li.innerHTML += "<br />";
document.getElementById("disp").appendChild(li);
} else if (ret.type.match('text.*')) {
document.getElementById("disp").innerHTML = ret.val;
}
}
</script>
(1)Drop領域にドロップした際のファイルの読み取り処理
Drag and Drop APIを使用し、ファイルがドロップされた際の処理を記述している。今回はワーカー側でファイル読み取り処理を実施するので、ドロップされたファイルを1つずつワーカー側に渡している。その際に使用されるpostMessageメソッドの引数で、「ファイル(file)」と「ファイルを読み込むためのファイルのMIMEタイプ(type)」の2つを渡している。
(2)ワーカーからのメッセージ取得時の処理
ワーカーからメッセージを取得したときの処理をonmessageメソッドに記載している。今回は、ワーカーから取得したメッセージのデータは、「MIMEタイプ(type)」と「読み込んだファイル・データ(val)」となる。
MIMEタイプを利用して画像ファイルかテキスト・ファイルかを判定し、それぞれ向けに後続の処理を分けている。テキスト・ファイルの場合は読み込んだテキストをそのまま表示できるが、画像ファイルの場合は<img>要素のsrc属性に読み込んだ画像データ(=データURI*1形式の文字列)をセットする必要があるためだ。
*1 <img>要素のsrc属性や、CSSのbackgroundプロパティなどのコンテキストに、画像などのデータを埋め込む手段。例えば「<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAFSDNYfAAAAaklEQVR42u3XQQrAIAwAQeP%2F%2F6wf8CJBJTK9lnQ7FpHGaOurt1I34nfH9pMMZAZ8BwMGEvvh%2BBsJCAgICLwIOA8EBAQEBAQEBAQEBK79H5RfIQAAAAAAAAAAAAAAAAAAAAAAAAAAAID%2FABMSqAfj%2FsLmvAAAAABJRU5ErkJggg%3D%3D">」のような形で埋め込める。詳しくは「MSDN:data Protocol」を参照してほしい。
続いて、ワーカー側で使用されるJavaScriptファイルのコードだ。
onmessage = function (event) {
// (1)同期APIの用意
var reader = new FileReaderSync();
var datafiles = event.data;
var val = "";
// (2)ファイルの種類を判定したうえでの処理
if (datafiles.type.match('image.*')) {
val = reader.readAsDataURL(datafiles.file);
}
if (datafiles.type.match('text.*')) {
val = reader.readAsText(datafiles.file, 'shift-jis');
}
// (3)UIスレッドに処理結果を送る
postMessage({ "type": datafiles.type, "val": val });
}
(1)同期APIの用意
前回紹介したFile APIのサンプルでは非同期APIであるFileReaderインターフェイスを使用して作成したが、今回はワーカー上でのみ使用可能なFileReaderSyncインターフェイスを使用する。FileReaderSyncインターフェイスでは、FileReaderインターフェイス同様にreadAsBinaryStringメソッド、readAsTextメソッド、readAsDataURLメソッドを使用する。非同期APIであるFileReaderの場合は、イベント経由でしか値を取得できなかったが、FileReaderSyncの場合は、メソッドの戻り値として値を取得できるのが特徴だ。
上記では、ワーカーが用意された段階で、FileReaderSyncインターフェイスのオブジェクトを生成している。
(2)ファイルの種類を判定したうえでの処理
UIスレッドから取得したメッセージ・データからMIMEタイプを確認し、画像ファイルの場合は、readAsDataURLメソッドで画像ファイルを読み込んだ結果を、変数valに設定している。テキスト・ファイルの場合は、readAsTextメソッドでテキスト・ファイルを読み込んだ結果を変数valに設定する。
(3)UIスレッドに処理結果を送る
UIスレッドに「読み込み結果(val)」と「MIMEタイプ(type)」を送っている。
実行結果は先ほどの図4〜6のとおりである。ケース・バイ・ケースだが、例えば大量のファイルを扱う場合や、サイズの大きいファイルを複数扱う場合などには、ワーカーを使うということも含めて検討してみるとよいだろう。
■まとめ
今回は、JavaScriptをバックグラウンドで実行するWeb Workersについて紹介した。実装も進んでおり、処理を分離させつつ、ローカルのリソースも効果的に使えるWeb WorkersはHTML5アプリケーションのバックボーンとして活躍するだろう。
ぜひ基礎を押さえて、効果的なHTML5アプリケーション開発に役立ててもらえれば幸いだ。
最終回となる次回は、ブラウザ上で動作するキー・バリュー型のデータベース「Indexed DataBase」を紹介する。お楽しみに。
Copyright© Digital Advantage Corp. All Rights Reserved.