ぐるぐるループしながら処理を行う際に、一定時間 間をおいてから次の処理をしたい場合は多々あります。
他の言語ですと sleep() や wait() を使えばいいのですが、残念JavaScript にはそのようなコマンドはありません。まぁJSの使われ所を考えると当然かもしれませんが。
もちろん JavaScript では実現できないって意味じゃありません。setTimeout() があります。でもちょっと実装方法が違うので、最初のうちは少しだけ悩んじゃうんですよね。
先日そういった処理を実装する方法を質問されて、ちょっと説明に苦心しました。忘れてますね~(苦笑。
ということで本日は説明した時の備忘録。
まずそれぞれの「待つコマンド」の違いを見てみましょう。
- 他の言語の sleep() や wait() の役割
- 指定された時間(多くはミリ秒単位)待ってから次の行を実行する。
- JavaScript のsetTimeout() の役割
- 一定時間後に実行する処理を予約登録し、そのまま次の行を実行する。(ビデオの録画予約を登録したらさっさと次の用事を始める人間の動きに似ています。)
この違いが分からないうちは、
alert("始めるよ");
for( i=0; i<10; i++) {
setTimeout("alert('Go!')",2000);
}
alert("お待たせ");
なんてコードを書いてしまいますね。この例ですと「始めるよ」を表示した後(すごい勢いで10件分の予約登録だけしてから)すぐに「お待たせ」が表示され、2秒後に連続10回「Go!」が表示されます。
以上、説明編。
ではどう実装するかですが、例えば配列にパラメータを突っ込んでおいて、その件数分ループしながら4秒に1回処理を行う、というありがちな処理の場合は下記のような実装でどうでしょう。
// パラメータ
arr = [1,3,24,126,326];
// 実処理の実行
act();
function act() {
// パラメータが無くなっていれば終了
if(arr.length==0) return;
// 配列の先頭を使う
param = arr[0];
//TODO: 何かの処理
alert(param);
// 処理済みのパラメータ削除
arr.shift();
// 次の回の実行予約
setTimeout(function(){
act();
}, 4000);
// これで1回の処理は終了
}
一見 再帰的に呼び出しているように見えるので、「パラメータの件数が増えた時に大丈夫?永久ループでは使えない?」と思われがちですが、あくまで後続の関数呼び出しは「予約登録」を行っているだけで自関数内から呼んでいませんので、再帰ではありません。
ところで若いうちはJavaScriptにsleepもwaitもないと分かると、
alert("始めるよ");
for( i=0; i<10; i++) {
alert("処理します");
// 待ちのための空ループ
for( j=0; j<10000; j++) {}
}
alert("お待たせ");
とやりがちです。こりゃあもう間違いのセオリーですな(笑。
これはダメです。なぜかというと、このやり方だと割り込みが入らないので、同じページで例えばonclick()なんかを仕込んでおいたとしても処理実行中は聞き入れてくれません。処理が終わるまで固まりっぱなしです。
まぁこういう方式もバッチ処理ならこれもアリかもしれません。JavaScriptでバッチを組むなら・・・。