パスワードを忘れた? アカウント作成
43463 journal

airheadの日記: memo: キュー付きsetTimeout()

日記 by airhead

先日、いくつかの処理をsetTimeout()でそれぞれ間隔をあけて実行させようとしていたときのこと。初めから待ち時間をそれぞれずらして設定すれば済む話だが、一つの処理までの待ち時間が経過して呼ばれたときに、次の処理を待ち時間とともにsetTimeout()で設定する方が後々変更しやすいか、と思った。

なんとなく書いているうちに、一応思ったとおりに動くが何とも読みづらいものができてしまった。for 文を setTimeout に変換する - IT戦記の「ここで戦略を練る」「こうか」に書かれているようなやつ(を、もっと不細工にしたようなもの)。

そこでsetTimeout()と同じように引数を渡すとキューに溜め込んで順次setTimeout()に設定するような関数があれば、for文を変換しなくてもいいし楽できるんじゃないかと思った。今時のライブラリを探せば似たようなものはあるのかもしれないが、クロージャの復習もかねて書いてみたのが前エントリのコード。

queue.set()にsetTimeout()と同じような引数を渡していくと順番待ちの列を作る。引数が渡されたときすでに待ち状態に入っていなければ、列の先頭を(次順設定手続きとともに)setTimeout()で設定する。仕込み部分が妙に長く、これまた不細工になってしまったが、とりあえず思い描いたような動作はしている。

ex.2では待ち時間ゼロにしているが、順次出力されるのが目視できるほど遅い。Firebugで調べてみたところ、この要因は_log内のDOMアクセスの遅さ[*]であって、queue.set()が特別に遅いわけではないようだ。

以下のループを例にして、組み入れ方を示しておこう (ex.2)。

for(var i=0; i<30; i++){
    _log("i"+i);
}

これとsetTimeout()を組み合わせて同様の出力を得たいとき、下記のように単純に包むと、引数として渡される関数が実行される頃には変数iの値も変わっている可能性がある(実際問題として、変わっている可能性が極めて高い)。

for(var i=0; i<30; i++){
    setTimeout(function(){_log("i"+i);});
}

setTimeoutを匿名関数で包むと、setTimeoutによるグローバルから内側の関数への参照とあわさることで、クロージャ構造になる。forループに使われている変数iの値は、その都度用意される匿名関数のローカル変数i(同名だが別の変数)に渡されて保持、クロージャ実行時に参照される。

for(var i=0; i<30; i++){
    (function(i){
        setTimeout(function(){_log("i"+i);});
    })(i);
}

あとはキューを用意する仕込み部分を頭に加えて setTimeout を queue.set で置き換えれば、設定順と実行順が一致しない可能性がある直接設定にかわって、間接設定順に順次自動設定・実行されることになる。

var createQueuedTimer = function(){
    // snip
}
var queue = createQueuedTimer();

for(var i=0; i<30; i++){
    (function(i){
        queue.set(function(){_log("i"+i);});
    })(i);
}

for文は元のままでよく、ループをネストさせるのも難しくない (ex.3)。

[*] setTimeout()による待ち時間以外の実行時間の95%以上を占めているのは_log内の ta.value = rslt; のようだ。テキストエリアへの出力に改行が多くなるにしたがって速度低下を起こすようで、直前行を rslt += str; として改行を省くと元の1/3程度に速くすることはできるが、それでもなお実行時間の90%前後を占め、順次出力される様子は相変わらず目視できる。一方で、スクロールさせるための直後行 ta.scrollTop = ta.scrollHeight - tach; をコメントアウトしても、実行時間にはほとんど影響しない。

12/22 21:50 コード修正: コピペ再利用時のミス発見。ex.3のループ for(var j=0; j<4; j++){ ... } 内において (function(j, k){ ... })(j, k); としたいところが、前後とも (i, j) にしてしまっていた。
12/23 20:00 コード修正: DOM読み出しを減らすなど微調整。効果はスズメの涙ほど。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
typodupeerror

アレゲはアレゲを呼ぶ -- ある傍観者

読み込み中...