2014
11
30
11
30
JavaScript: jQuery.Deferredで非同期処理を書く
HTML5で使えるファイル読み込みのサンプルがHTML5ROCKSにある。ローカルの画像ファイルを読み込んでブラウザに表示するというもの。
http://www.html5rocks.com/ja/tutorials/file/dndfiles/#toc-reading-files
これを手本に自分の作ったJavaScriptでのJPEG縮小ライブラリの使用サンプルを書いたが、ちょっと処理の流れを変更したところがある。
手本はforループで、img要素を作ってそこに画像データを読み込ませている。この画像データを読み込む部分は同期処理ではなく、img.onloadに仕込まれた非同期処理で行われる。つまり画像データ読み込みが終わらないうちに次のループが始まり、画像データを読み込むプロセスがさらに追加される。画像ファイルを選択した数だけ画像データを読み込むプロセスが同時に発生することになる。これはCPUやメモリへの負荷となり環境によってはブラウザをダウンさせるかもしれない。一つのimg要素を作ったらそのonloadイベント内の最後に実行するコールバック関数を用意し、一つのimg要素での画像読み込み処理が終わるたびに新たなimg要素を作るようにしたらどうだろう。試してみたらCPU使用のピークが改造前と改造後で50%と30%だった。一応効果はあるんだろうか。だがそんなスクリプトを書くと非同期処理内で再帰が必要になったりで少し読みづらくなったりする。
imageReader - recur
こんな状況でjQueryに用意されたdeferredを知った。
爆速でわかるjQuery.Deferred超入門
使ってみた。使い方は上記のYahooを参考に。関数内でdeferredオブジェクト(var dとする)を作って、その関数内で終了をキャッチしたい非同期処理の最後にd.resolve()を置いて、関数の最後でd.promise()を返す。あとはその関数を実行するだけ。順番にファイル処理をしたい場合はクロージャを使って関数を書いておいて、
foo(f[0])().then(foo(f[1])).then(foo(f[2])).then(foo(f[3]));
というふうにthenでつないでいく。これで順番にファイル処理が行われる。コールバックを使って順番に行っていた処理が、コールバックの代わりにdeferredに制御してもらうことで順番に処理ができるように書き換えられる。
任意の要素数だけthenをつなげたかった。for文で追加していくなど考えてみたがfoo().then(foo)と書いた時点で実行される。文字列でこの式を書いてfor文で必要な数だけthenをつなぎevalで実行した。ここらはevalなしでfor文でthenつなぎをできないか要検討。
imageReader - deferred
JPEGを読み込んで縮小するライブラリの使用サンプルをdeferredを使って書いてみた。
https://github.com/hMatoba/JavaScript-MinifyJpegAsync/blob/master/deferred.html
http://www.html5rocks.com/ja/tutorials/file/dndfiles/#toc-reading-files
これを手本に自分の作ったJavaScriptでのJPEG縮小ライブラリの使用サンプルを書いたが、ちょっと処理の流れを変更したところがある。
手本はforループで、img要素を作ってそこに画像データを読み込ませている。この画像データを読み込む部分は同期処理ではなく、img.onloadに仕込まれた非同期処理で行われる。つまり画像データ読み込みが終わらないうちに次のループが始まり、画像データを読み込むプロセスがさらに追加される。画像ファイルを選択した数だけ画像データを読み込むプロセスが同時に発生することになる。これはCPUやメモリへの負荷となり環境によってはブラウザをダウンさせるかもしれない。一つのimg要素を作ったらそのonloadイベント内の最後に実行するコールバック関数を用意し、一つのimg要素での画像読み込み処理が終わるたびに新たなimg要素を作るようにしたらどうだろう。試してみたらCPU使用のピークが改造前と改造後で50%と30%だった。一応効果はあるんだろうか。だがそんなスクリプトを書くと非同期処理内で再帰が必要になったりで少し読みづらくなったりする。
imageReader - recur
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>
<script>
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var images = [];
for (var i=0; i<files.length; i++) {
// Only process image files.
if (files[i].type.match('image.*')) {
images.push(files[i]);
}
}
showImage(images);
}
function showImage(images) {
var f;
if (images.length == 0) {
return;
} else {
f = images.shift();
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
showImage(images);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
こんな状況でjQueryに用意されたdeferredを知った。
爆速でわかるjQuery.Deferred超入門
使ってみた。使い方は上記のYahooを参考に。関数内でdeferredオブジェクト(var dとする)を作って、その関数内で終了をキャッチしたい非同期処理の最後にd.resolve()を置いて、関数の最後でd.promise()を返す。あとはその関数を実行するだけ。順番にファイル処理をしたい場合はクロージャを使って関数を書いておいて、
foo(f[0])().then(foo(f[1])).then(foo(f[2])).then(foo(f[3]));
というふうにthenでつないでいく。これで順番にファイル処理が行われる。コールバックを使って順番に行っていた処理が、コールバックの代わりにdeferredに制御してもらうことで順番に処理ができるように書き換えられる。
任意の要素数だけthenをつなげたかった。for文で追加していくなど考えてみたがfoo().then(foo)と書いた時点で実行される。文字列でこの式を書いてfor文で必要な数だけthenをつなぎevalで実行した。ここらはevalなしでfor文でthenつなぎをできないか要検討。
imageReader - deferred
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>
<script>
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
if (files.length) {
var str = "showImage(files[0])()";
for (var i = 1, f; f = files[i]; i++) {
str += ".then(showImage(files[" + i + "]))";
}
console.log(str);
eval(str);
}
}
function showImage(f) {
return function() {
var d = new $.Deferred();
// Only process image files.
if (!f.type.match('image.*')) {
d.resolve();
return;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
d.resolve();
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
return d.promise();
};
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
JPEGを読み込んで縮小するライブラリの使用サンプルをdeferredを使って書いてみた。
https://github.com/hMatoba/JavaScript-MinifyJpegAsync/blob/master/deferred.html
スポンサーサイト