WEBアプリで、要素をドラッグしているときや、DIVを連動させてスクロールしている最中に、画面更新が必要になったりします。たとえば、プレースホルダーの描画や、動的な並べ替え、またはビューポートのサムネイル表示などですね。
ところがこれらのイベントは短い時間に大量に発生しますから、イベント発生の度に画面更新をやっていると、軽い処理なら気になりませんが、少々重たく複雑なことになると、操作がカクカクして(こっちはイライラw)良かれと思ってやってる処理が逆に使いづらくなったりしますよね。
そこでコレを回避するためのコードを書きました。簡単なコードですけど効果絶大でしたのでご紹介。ま、みんなやってるのかもしれませんけどw
/**
* 後でやる。一回だけやる。
* やる前に何回言われても一回だけ。
*
* @param job 後で実行する処理
* @param tmo 実行待ち時間
*/
function doLater(job, tmo) {
//処理が登録されているなら
//タイマーをキャンセル
var tid = doLater.TID[job];
if(tid != undefined) {
window.clearTimeout(tid);
}
//タイムアウト登録する
doLater.TID[job] = window.setTimeout(
function() {
//実行前にタイマーIDをクリア
doLater.TID[job] = undefined;
//登録処理を実行
job.call();
}, tmo);
}
//処理からタイマーIDへのハッシュ
doLater.TID = {};
使い方の例はこちら↓
function dataTable_onScroll() {
doLater( function () {
updateScreen();//画面更新処理
}, 300);//処理内容によって調整
}
指定時間後に処理が実行されるようにタイマー登録し、実行待ち状態で再び同じ処理を登録しようとすると、先に登録したタイマーをキャンセルし、新たに登録します。つまり実際に実行されるのは、最後に登録されてから指定時間経過後となります。
要するにwindow.setTimeoutを利用した遅延実行処理なんですが、タイマーIDを処理(無名関数)をキーとしたハッシュに保存しておくところがミソといえばミソかな。
でも、クロージャは使わないようにしましょう。参照しているクロージャの値が違っていても、登録されている関数オブジェクトは同じものと判定されますので、やるべき処理がキャンセルされてしまう可能性があります。同じ理由から、実行時の状況に依存しない処理であるべきと言い換えられるかもしれません。
以下の例だと、マウスの動きによって更新する場所を探してそこだけを更新するように読めますが、マウスの動きによっては更新されない場所が出てきそうです。
↓ダメな例です。
function dataTable_onScroll() {
//更新すべき列のIDを取得
var column_id = getMouseOverColumnId();
doLater( function () {
//クロージャ使うと
//処理が抜けるかもしれません。
updateScreen(column_id);//列を更新
}, 300);//処理内容によって調整
}
これを回避するには、登録するためのキーを指定できるようにしておいて、省略時には関数オブジェクトをキーとして使用するようにすれば良いと思いますが、そこまで複雑な需要は無いかもですね。
他の拡張案としては、時間指定をしなければ、関数オブジェクトの分量から適当な遅延時間を算出するとか、そういうのもありかな。
最近のコメント