JavaScriptのループの中でスリープしたい
JavaScriptを使っている時に、ループの処理の中でスリープをかけたい場合ってありますよね。
外部のAPIを繰り返し呼び出すときとか。
しかし、JavaScriptには他の言語でよくある「sleep()」のようなものはありません。
ですので、こんな感じで書くことはできません。
// 注意:動きません!
for (var i = 0; i < 10; i++) {
doSomething();
sleep(1000); // 1000ミリ秒スリープする
}
そこでsetTimeout()の出番
JavaScriptで一定時間後に処理を実行したい場合は、setTimout()を使います。
setTimeout(func, delay);
「なるほど!そうかsetTimeout()か!それじゃ、こう書くんだね!」
と、早とちりをして以下のように書いてしまうことがけっこうあります。
// 注意:これも狙った動きはしません!
for (var i = 0; i < 10; i++) {
setTimeout(doSomething, 1000);
}
残念ながら、このコードは1秒おきにdoSomething()が10回呼ばれるのではなく、
1秒後にdoSomething()がまとめて10回呼ばれるだけです。
正しく、1秒おきにdoSomething()を10回呼ぶには以下のように書く必要があります。
function doSomethingLoop(maxCount, i) {
if (i <= maxCount) {
doSomething();
setTimeout(function(){doSomethingLoop(maxCount, ++i)}, 1000);
}
}
doSomethingLoop(10, 0);
これなら、正しく動きます。
※setInterval()を使った方法は、下の@3upjpさんのコメントを参照。
setTimeoutによるスリープ処理の問題点(と私が思うもの)
が、上述のコードはなんと言うか、ひじょーーに読みづらい。
パッと見でこれが、
- ループで
- doSomething()を1000ミリ秒ごとに10回呼び出したりしている
処理だと理解できますか?
経験豊富なJavaScriptエンジニアの方は読み取れるかしれません。
ですが、実現したいことに対して、あまり直感的なコードではないことは同意していただけるのではないでしょうか。
で、
前置きが長くなりました。
- もっと直感的に、通常のループ処理っぽく書きたい!
- 再帰処理とかあんまり書いていると、頭がオーバーフローしてしまう
と思い、以下の様な関数を書いてみました。
function loopSleep(_loopLimit,_interval, _mainFunc){
var loopLimit = _loopLimit;
var interval = _interval;
var mainFunc = _mainFunc;
var i = 0;
var loopFunc = function () {
var result = mainFunc(i);
if (result === false) {
// break機能
return;
}
i = i + 1;
if (i < loopLimit) {
setTimeout(loopFunc, interval);
}
}
loopFunc();
}
この関数を使って、スリープ付きのループ処理を書いてみます。
// 例(10回ループを、1000ミリ秒毎に実行します)
loopSleep(10, 1000, function(i){
doSomething();
});
どうでしょうか?
私としては、「単純にsetTimeoutと再帰呼び出しを組み合わせたやり方」よりも、だいぶ分かりやすく書けるようになったと思います。
continueやbreakと同等のことをしたい場合は、以下のように書きます
// 例(10回ループを、1000ミリ秒毎に実行します)
loopSleep(10, 1000, function(i){
if (i == 7) {
// breakと同等
return false;
} else if (i % 2 == 0) {
// continueと同等
return;
}
doSomething();
});
以上です。
問題点、改善点などありましたらご指摘ください!