前回まででJSDeferredの基本的な使いかたを説明しました。
今回は
UIスレッド
JavaScriptが担当するアプリケーションユーザインターフェイスの
JavaScriptを実行するブラウザには、
JavaScriptは、
UIスレッドの実行をできるだけ止めない
UIスレッドを長時間
var n = 1000000;
for (var i = 0; i < n; i++) {
// do something
}
では、
setTimeout()関数は指定時間後に指定した関数を実行する関数ですが、
setTimeout(function () {
// do something
}, 0);
例えば、
var n = 1000000;
var i = 0;
setTimeout(function () {
if (i < n) {
// do something
i++;
setTimeout(arguments.callee, 0);
}
}, 0);
単純にforのループの切れ目でsetTimeoutするようにしただけですが、
全体が非同期の流れになっているので、
JSDeferredを使うようにする
では、
var n = 1000000;
var i = 0;
next(function () {
if (i < n) {
// do something
return next(arguments.callee);
}
}).
next(function () {
alert('done');
});
とりあえずこれで、
とはいっても、
var n = 1000000;
loop(n, function (i) {
// do something
}).
next(function () {
alert('done');
});
とても簡単になりました! UIもブロックせず簡潔です。ただし、
さらに効率よくするために
今までの例では、
そこで、
function aloop (n, f) {
var i = 0, end = {}, ret = null;
return Deferred.next(function () {
var t = (new Date()).getTime();
divide: {
do {
if (i >= n) break divide;
ret = f(i++);
} while ((new Date()).getTime() - t < 20);
return Deferred.call(arguments.callee);
}
});
}
このような関数を利用することで、
パフォーマンス改善にJSDeferredを使う理由
これだけ簡単にループを分割し、
もしここでsetTimeout()によるプリミティブな方法をしていると、
その他の応用
これまでXMLHttpRequestやsetTimeoutの例ばかりでしたが、
クリックイベントをDeferred化してステップ実行を実装してみます。
function onclick (element) {
var d = new Deferred();
element.addEventListener("click", function (e) {
element.removeEventListener("click", arguments.callee, false);
d.call(e);
}, false);
return de;
}
loop(10, function (n) {
alert(n);
return onclick(document.body);
});
たったこれだけでクリックするごとにワンステップループをすすめるコードを記述することができます。簡単ですね。おこなっていることは前回までの例と同じで、
まとめ
4回にわたり、
JSDeferredによって、
- 非同期処理を簡潔になる
- パフォーマンス改善がしやすくなる
JSDeferredを知らない人がコードを読むと多少意味がわかりにくくなってしまう欠点もありますが、
最初のバージョンが出てから既に2年以上経過していますし、