Help us understand the problem. What is going on with this article?

JavaScriptでループ中にスリープしたい。それも読みやすいコードで

More than 3 years have passed since last update.

JavaScriptのループの中でスリープしたい

JavaScriptを使っている時に、ループの処理の中でスリープをかけたい場合ってありますよね。
外部のAPIを繰り返し呼び出すときとか。

しかし、JavaScriptには他の言語でよくある「sleep()」のようなものはありません。
ですので、こんな感じで書くことはできません。

if_js_had_a_sleep.js
// 注意:動きません!
for (var i = 0; i < 10; i++) {
    doSomething();
    sleep(1000); // 1000ミリ秒スリープする
}

そこでsetTimeout()の出番

JavaScriptで一定時間後に処理を実行したい場合は、setTimout()を使います。

setTimeout(func, delay);

「なるほど!そうかsetTimeout()か!それじゃ、こう書くんだね!」
と、早とちりをして以下のように書いてしまうことがけっこうあります。

setTimeout_bad_sample.js
// 注意:これも狙った動きはしません!
for (var i = 0; i < 10; i++) {
    setTimeout(doSomething, 1000);
}

残念ながら、このコードは1秒おきにdoSomething()が10回呼ばれるのではなく、
1秒後にdoSomething()がまとめて10回呼ばれるだけです。

正しく、1秒おきにdoSomething()を10回呼ぶには以下のように書く必要があります。

setTimeout_valid_sample.js
function doSomethingLoop(maxCount, i) {
  if (i <= maxCount) {
    doSomething();
    setTimeout(function(){doSomethingLoop(maxCount, ++i)}, 1000);
  }
}

doSomethingLoop(10, 0);

これなら、正しく動きます。
※setInterval()を使った方法は、下の@3upjpさんのコメントを参照。

setTimeoutによるスリープ処理の問題点(と私が思うもの)

が、上述のコードはなんと言うか、ひじょーーに読みづらい。
パッと見でこれが、

  • ループで
  • doSomething()を1000ミリ秒ごとに10回呼び出したりしている

処理だと理解できますか?

経験豊富なJavaScriptエンジニアの方は読み取れるかしれません。
ですが、実現したいことに対して、あまり直感的なコードではないことは同意していただけるのではないでしょうか。

で、

前置きが長くなりました。

  • もっと直感的に、通常のループ処理っぽく書きたい!
  • 再帰処理とかあんまり書いていると、頭がオーバーフローしてしまう

と思い、以下の様な関数を書いてみました。

loopSleep.js
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();
}

この関数を使って、スリープ付きのループ処理を書いてみます。

loopSleep_client_sample.js
// 例(10回ループを、1000ミリ秒毎に実行します)
loopSleep(10, 1000, function(i){
  doSomething();
});

どうでしょうか?
私としては、「単純にsetTimeoutと再帰呼び出しを組み合わせたやり方」よりも、だいぶ分かりやすく書けるようになったと思います。

continueやbreakと同等のことをしたい場合は、以下のように書きます

loopSleep_client_sample2.js
// 例(10回ループを、1000ミリ秒毎に実行します)
loopSleep(10, 1000, function(i){
  if (i == 7) {
    // breakと同等
    return false;
  } else if (i % 2 == 0) {
    // continueと同等
    return;
  }
  doSomething();
});

以上です。

問題点、改善点などありましたらご指摘ください!

akyao
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした