JavaScriptループに遅延を追加するにはどうすればよいですか?


345

whileループ内に遅延/スリープを追加したいと思います:

私はこのようにしてみました:

alert('hi');

for(var start = 1; start < 10; start++) {
  setTimeout(function () {
    alert('hello');
  }, 3000);
}

最初のシナリオのみが当てはまります。を表示した後alert('hi')、3秒間待機してからalert('hello')表示されますが、その後alert('hello')は常に繰り返し表示されます。

私が欲しいのは、alert('hello')3秒後に「」が表示され、alert('hi')その後3秒間待つ必要があるということalert('hello')です。

回答:


749

setTimeout()この関数は非ブロックで、すぐに戻ります。したがって、ループは非常に高速に反復し、3秒のタイムアウトトリガーを次々と開始します。そのため、3秒後に最初のアラートがポップアップ表示され、残りのすべてのアラートは遅れることなく連続して続きます。

代わりに、次のようなものを使用することができます。

var i = 1;                  //  set your counter to 1

function myLoop() {         //  create a loop function
  setTimeout(function() {   //  call a 3s setTimeout when the loop is called
    console.log('hello');   //  your code here
    i++;                    //  increment the counter
    if (i < 10) {           //  if the counter < 10, call the loop function
      myLoop();             //  ..  again which will trigger another 
    }                       //  ..  setTimeout()
  }, 3000)
}

myLoop();                   //  start the loop

また、自己呼び出し関数を使用して、反復回数を引数として渡すことにより、それを強化することもできます。

(function myLoop(i) {
  setTimeout(function() {
    console.log('hello'); //  your code here                
    if (--i) myLoop(i);   //  decrement i and call myLoop again if i > 0
  }, 3000)
})(10);                   //  pass the number of iterations as an argument


26
これを実装するために再帰を使用すると、最終的にはスタックオーバーフローの影響を受けませんか?100万回の反復を実行したい場合、これを実装するより良い方法は何でしょうか?たぶん、以下のAbelのソリューションのように、setIntervalをクリアしてください。
Adam

7
@Adam:私の理解では、setTimeoutは非ブロッキングであるため、これは反論ではありません。スタックウィンドウは各setTimeoutの後に閉じ、実行を待機しているsetTimeoutは1つだけです...そうですか?
Joe

3
for inループのようなオブジェクトを反復するとき、これはどのように機能しますか?
vsync

1
@vsync調査Object.keys()
ブレーデンベスト

1
@joeyと混同setTimeoutしていsetIntervalます。コールバックが呼び出されると、タイムアウトは暗黙的に破棄されます。
cdhowie 2018年

72

次のようなものを試してください:

var i = 0, howManyTimes = 10;
function f() {
    alert( "hi" );
    i++;
    if( i < howManyTimes ){
        setTimeout( f, 3000 );
    }
}
f();

69

ES6を使用している場合、letこれを達成するために使用できます。

for (let i=1; i<10; i++) {
    setTimeout( function timer(){
        alert("hello world");
    }, i*3000 );
}

let行うことは宣言されi、それぞれについての繰り返しではなく、ループ。このように、渡されるものsetTimeoutはまさに私たちが望むものです。


1
感謝!この方法を自分で考えたことはなかったでしょう。実際のブロックのスコープ。想像してみてください
ソフィアゴールド


1
@Flame_Phoenixメモリ割り当ての問題は何ですか?
キャッスル2017

1
setTimeout呼び出しi*3000は、ループ内で引数の値を同期的に計算し、それをsetTimeout値で渡します。の使用letはオプションであり、質問と回答とは無関係です。
traktor53

@Flame_Phoenixは、このコードに問題があると述べました。基本的に最初のパスでタイマーを作成し、ループを条件(i < 10)で終了するまでループを繰り返します。これにより、複数のタイマーが並行して動作し、メモリ割り当てが作成されます。反復回数が多いとパフォーマンスが低下します。
XCanG

63

ES7は、より良い方法theresのので待つループを:

// Returns a Promise that resolves after "ms" Milliseconds
function timer(ms) {
 return new Promise(res => setTimeout(res, ms));
}

async function load () { // We need to wrap the loop into an async function for this to work
  for (var i = 0; i < 3; i++) {
    console.log(i);
    await timer(3000); // then the created Promise can be awaited
  }
}

load();

エンジンがawaitパーツに到達すると、タイムアウトを設定し、の実行を停止しasync functionます。その後、タイムアウトが完了すると、その時点で実行が継続されます。(1)ネストされたループ、(2)条件付き、(3)ネストされた関数を遅延させることができるので、これは非常に便利です。

async function task(i) { // 3
  await timer(1000);
  console.log(`Task ${i} done!`);
}

async function main() {
  for(let i = 0; i < 100; i+= 10) {
    for(let j = 0; j < 10; j++) { // 1
      if(j % 2) { // 2
        await task(i + j);
      }
    }
  }
}
    
main();

function timer(ms) { return new Promise(res => setTimeout(res, ms)); }

MDNに関するリファレンス

ES7がNodeJSと最新のブラウザーでサポートされるようになりましたが、ES7をBabelJSでトランスパイルして、どこでも実行できるようにすることもできます。


それは私にとってはうまくいきます。ループを解除する場合は、awaitを使用するときにどうすればよいですか。
Sachin Shah

@sachin break;多分?
ジョナスウィルムス

この解決策をありがとう。既存のすべての制御構造を使用すると便利で、継続を発明する必要はありません。
Gus

これでもまださまざまなタイマーが作成され、順番にではなく異なる時間に解決されますか?
David Yell

@david um nope?実際にコードを実行しましたか?
ジョナスウィルムス

24

もう1つの方法は、タイムアウトまでの時間を掛けることですが、これはスリープは異なります。ループ後のコードはすぐに実行され、コールバック関数の実行のみが延期されます。

for (var start = 1; start < 10; start++)
    setTimeout(function () { alert('hello');  }, 3000 * start);

最初のタイムアウトは3000 * 1に、2番目はに、というように設定さ3000 * 2れます。


2
startこのメソッドを使用して関数内で確実に使用することはできないことを指摘する価値があります。
DBS 2015年

2
悪い習慣-不必要なメモリ割り当て。
Alexander Trakhimenok

創造性に賛成票を投じてください、しかしそれはいまいましい悪い習慣です。:)
Salivan

2
なぜそれが悪い習慣であり、なぜメモリ割り当ての問題があるのですか?この答えは同じ問題を抱えていますか?stackoverflow.com/a/36018502/1337392
Flame_Phoenix 2016

1
@Flame_Phoenixプログラムはループごとに1つのタイマーを保持し、すべてのタイマーが同時に実行されるため、これは悪い習慣です。したがって、1000回の反復がある場合、最初は同時に1000個のタイマーが実行されます。
ジョアキム

16

これは動作します

for (var i = 0; i < 10; i++) {
  (function(i) {
    setTimeout(function() { console.log(i); }, 100 * i);
  })(i);
}

このフィドルを試してください:https : //jsfiddle.net/wgdx8zqq/


1
ただし、これはほぼすべてのタイムアウト呼び出しをほぼ同時にトリガーします
Eddie

私が言う唯一のことは、私はこの方法でクラックし、使用しました$.Deferredが、それを機能させるにはいくつかの異なるシナリオでした。
ArifMustafa

15

私はあなたがこのようなものが必要だと思います:

var TimedQueue = function(defaultDelay){
    this.queue = [];
    this.index = 0;
    this.defaultDelay = defaultDelay || 3000;
};

TimedQueue.prototype = {
    add: function(fn, delay){
        this.queue.push({
            fn: fn,
            delay: delay
        });
    },
    run: function(index){
        (index || index === 0) && (this.index = index);
        this.next();
    },
    next: function(){
        var self = this
        , i = this.index++
        , at = this.queue[i]
        , next = this.queue[this.index]
        if(!at) return;
        at.fn();
        next && setTimeout(function(){
            self.next();
        }, next.delay||this.defaultDelay);
    },
    reset: function(){
        this.index = 0;
    }
}

テストコード:

var now = +new Date();

var x = new TimedQueue(2000);

x.add(function(){
    console.log('hey');
    console.log(+new Date() - now);
});
x.add(function(){
    console.log('ho');
    console.log(+new Date() - now);
}, 3000);
x.add(function(){
    console.log('bye');
    console.log(+new Date() - now);
});

x.run();

注:アラートを使用すると、アラートを閉じるまでJavaScriptの実行が停止します。あなたが要求したよりも多くのコードかもしれませんが、これは堅牢な再利用可能なソリューションです。


15

たぶんsetInteval。このような、

var period = 1000; // ms
var endTime = 10000;  // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
    alert('Hello');
    if(counter === endTime){
       clearInterval(sleepyAlert);
    }
    counter += period;
}, period);

3
SetTimeoutはsettintervalよりもはるかに優れています。グーグルそれとあなたは知っているでしょう
エアリー14

14
私はそれを少しググってみましたが何も見つかりませんでした、なぜsetIntervalが悪いのですか?リンクを頂けますか?または例?ありがとう
Marcs

エラーやブロックが発生した場合でも「スレッド」を生成し続けることがポイントだったと思いSetInterval()ます。
Mateen Ulhaq 2017年

8

ES6(ECMAScript 2015)では、ジェネレーターとインターバルを遅延して反復できます。

ECMAScript 6の新機能であるジェネレーターは、一時停止および再開できる関数です。genFuncを呼び出しても実行されません。代わりに、genFuncの実行を制御できる、いわゆるジェネレーターオブジェクトを返します。genFunc()は、最初は本体の先頭で中断されています。メソッドgenObj.next()は、次のyieldまでgenFuncの実行を継続します。 (ES6の探索)


コード例:

let arr = [1, 2, 3, 'b'];
let genObj = genFunc();

let val = genObj.next();
console.log(val.value);

let interval = setInterval(() => {
  val = genObj.next();
  
  if (val.done) {
    clearInterval(interval);
  } else {
    console.log(val.value);
  }
}, 1000);

function* genFunc() {
  for(let item of arr) {
    yield item;
  }
}

したがって、ES6を使用している場合は、遅延を伴うループを実現する最もエレガントな方法です(私の意見)。


4

RxJS 間隔演算子を使用できます。間隔はx秒ごとに整数を放出し、テイクは数値を放出する必要がある回数を指定します

Rx.Observable
  .interval(1000)
  .take(10)
  .subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>


4

ここにも私の2セントを投稿すると思いました。この関数は、遅延のある反復ループを実行します。このjsfiddleを参照してください。関数は次のとおりです。

function timeout(range, time, callback){
    var i = range[0];                
    callback(i);
    Loop();
    function Loop(){
        setTimeout(function(){
            i++;
            if (i<range[1]){
                callback(i);
                Loop();
            }
        }, time*1000)
    } 
}

例えば:

//This function prints the loop number every second
timeout([0, 5], 1, function(i){
    console.log(i);
});

以下と同等になります:

//This function prints the loop number instantly
for (var i = 0; i<5; i++){
    console.log(i);
}

4

これはBluebird Promise.delayと再帰で行います。

function myLoop(i) {
  return Promise.delay(1000)
    .then(function() {
      if (i > 0) {
        alert('hello');
        return myLoop(i -= 1);
      }
    });
}

myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>


2

ES6では、次のように実行できます。

 for (let i = 0; i <= 10; i++){       
     setTimeout(function () {   
        console.log(i);
     }, i*3000)
 }

ES5では、次のように実行できます。

for (var i = 0; i <= 10; i++){
   (function(i) {          
     setTimeout(function () {   
        console.log(i);
     }, i*3000)
   })(i);  
 }

その理由は、変数をグローバルに、またはブロックのスコープに関係なく関数全体にローカルに定義するキーワードletとは異なり、ブロックステートメントのスコープまたはそれが使用される式に限定される変数を宣言できるためですvar


1

関数をより再利用しやすくするために変数がパラメーターに抽出されたDaniel Vassalloの回答の修正バージョン:

まず、いくつかの重要な変数を定義しましょう:

var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;

次に、実行する関数を定義する必要があります。これには、必要に応じて、i、ループの現在のインデックス、およびループの長さが渡されます。

function functionToRun(i, length) {
    alert(data[i]);
}

自己実行バージョン

(function forWithDelay(i, length, fn, delay) {
   setTimeout(function () {
      fn(i, length);
      i++;
      if (i < length) {
         forWithDelay(i, length, fn, delay); 
      }
  }, delay);
})(startIndex, data.length, functionToRun, timeout);

機能バージョン

function forWithDelay(i, length, fn, delay) {
   setTimeout(function () {
      fn(i, length);
      i++;
      if (i < length) {
         forWithDelay(i, length, fn, delay); 
      }
  }, delay);
}

forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it

良いものと、グローバル変数なしで関数にデータを渡す方法
Sundara Prabu 2017年

1
   let counter =1;
   for(let item in items) {
        counter++;
        setTimeout(()=>{
          //your code
        },counter*5000); //5Sec delay between each iteration
    }

1

あなたはそれを行う:

console.log('hi')
let start = 1
setTimeout(function(){
  let interval = setInterval(function(){
    if(start == 10) clearInterval(interval)
    start++
    console.log('hello')
  }, 3000)
}, 3000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


アラートの代わりにコンソールログを使用する方が適切です。アラートを30分間閉じるのは面白くありませんでした;)
Hendry

うん。そうですか!しかし、要求が警告... huzがある
グエンBaのDanh -てFaic HN

jQueryをインポートする理由
エリアスソアレス

すみません...不要です。投稿内容がわかりません...これが最初です。
グエンバーダン-FAIC HN

0
/* 
  Use Recursive  and setTimeout 
  call below function will run loop loopFunctionNeedCheck until 
  conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay 
  reRunAfterMs miliseconds and continue loop
  tested code, thanks
*/

function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
 loopFunctionNeedCheck) {
    loopFunctionNeedCheck();
    var result = conditionCheckAfterRunFn();
    //check after run
    if (!result) {
        setTimeout(function () {
            functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
        }, reRunAfterMs);
    }
    else  console.log("completed, thanks");    
            //if you need call a function after completed add code call callback in here
}

//passing-parameters-to-a-callback-function
// From Prototype.js 
if (!Function.prototype.bind) { // check if native implementation available
    Function.prototype.bind = function () {
        var fn = this, args = Array.prototype.slice.call(arguments),
            object = args.shift();
        return function () {
            return fn.apply(object,
              args.concat(Array.prototype.slice.call(arguments)));
        };
    };
}

//test code: 
var result = 0; 
console.log("---> init result is " + result);
var functionNeedRun = function (step) {           
   result+=step;    
       console.log("current result is " + result);  
}
var checkResultFunction = function () {
    return result==100;
}  

//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100    
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));

//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/

7
関数名がひどいです。それが、このコードが非常に読みにくい主な理由です。
マークウォルターズ

0

これが、特定の条件で壊れる遅延のある無限ループを作成した方法です。

  // Now continuously check the app status until it's completed, 
  // failed or times out. The isFinished() will throw exception if
  // there is a failure.
  while (true) {
    let status = await this.api.getStatus(appId);
    if (isFinished(status)) {
      break;
    } else {
      // Delay before running the next loop iteration:
      await new Promise(resolve => setTimeout(resolve, 3000));
    }
  }

ここで重要なのは、タイムアウトによって解決する新しいPromiseを作成し、その解決を待つことです。

明らかに、そのための非同期/待機サポートが必要です。ノード8で動作します。


0

一般的な使用法では、「通常のループを忘れる」、および「setInterval」のこの組み合わせを使用すると、「setTimeOut」が含まれます。

        function iAsk(lvl){
            var i=0;
            var intr =setInterval(function(){ // start the loop 
                i++; // increment it
                if(i>lvl){ // check if the end round reached.
                    clearInterval(intr);
                    return;
                }
                setTimeout(function(){
                    $(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
                },50);
                setTimeout(function(){
                     // do another bla bla bla after 100 millisecond.
                    seq[i-1]=(Math.ceil(Math.random()*4)).toString();
                    $("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
                    $("#d"+seq[i-1]).prop("src",pGif);
                    var d =document.getElementById('aud');
                    d.play();                   
                },100);
                setTimeout(function(){
                    // keep adding bla bla bla till you done :)
                    $("#d"+seq[i-1]).prop("src",pPng);
                },900);
            },1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
        }

PS:(setTimeOut)の実際の動作を理解してください:それらはすべて同時に開始されます。

PS 2:タイミングループの例ですが、リアクションループの場合は、イベントを使用できます。asyncawaitを約束します。


0

<!DOCTYPE html>
<html>
<body>

<button onclick="myFunction()">Try it</button>

<p id="demo"></p>

<script>
function myFunction() {
    for(var i=0; i<5; i++) {
    	var sno = i+1;
       	(function myLoop (i) {          
             setTimeout(function () {   
             	alert(i); // Do your function here 
             }, 1000*i);
        })(sno);
    }
}
</script>

</body>
</html>


1
常にコードスニペットに少なくとも簡単な説明を提供してください。少なくとも他の人が質問に確実に対処できるようにしてください。
Hexfire 2018年

1
コード、彼らは将来の読者のために多くの情報を提供いけないとだけ答えはあなたが書いたものにいくつかの説明を提供してください奨励アレント
WhatsThePoint

0

私の知る限り、このsetTimeout関数は非同期に呼び出されます。あなたができることは、非同期関数内でループ全体をラップし、Promise示すようにsetTimeoutを含むをです。

var looper = async function () {
  for (var start = 1; start < 10; start++) {
    await new Promise(function (resolve, reject) {
      setTimeout(function () {
        console.log("iteration: " + start.toString());
        resolve(true);
      }, 1000);
    });
  }
  return true;
}

そして、次のようにrunを呼び出します。

looper().then(function(){
  console.log("DONE!")
});

非同期プログラミングをよく理解するために、少し時間をかけてください。


0

これを試してみてください

 var arr = ['A','B','C'];
 (function customLoop (arr, i) {
    setTimeout(function () {
    // Do here what you want to do.......
    console.log(arr[i]);
    if (--i) {                
      customLoop(arr, i); 
    }
  }, 2000);
})(arr, arr.length);

結果

A // after 2s
B // after 2s
C // after 2s

-1

配列のループに使用する関数は次のとおりです。

function loopOnArrayWithDelay(theArray, delayAmount, i, theFunction, onComplete){

    if (i < theArray.length && typeof delayAmount == 'number'){

        console.log("i "+i);

        theFunction(theArray[i], i);

        setTimeout(function(){

            loopOnArrayWithDelay(theArray, delayAmount, (i+1), theFunction, onComplete)}, delayAmount);
    }else{

        onComplete(i);
    }
}

次のように使用します。

loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
    //Do something with item
}, function(i){
    //Do something once loop has completed
}

-1

このスクリプトはほとんどの場合に機能します

function timer(start) {
    setTimeout(function () { //The timer
        alert('hello');
    }, start*3000); //needs the "start*" or else all the timers will run at 3000ms
}

for(var start = 1; start < 10; start++) {
    timer(start);
}

-1

これを試して...

var icount=0;
for (let i in items) {
   icount=icount+1000;
   new beginCount(items[i],icount);
}

function beginCount(item,icount){
  setTimeout(function () {

   new actualFunction(item,icount);

 }, icount);
}

function actualFunction(item,icount){
  //...runs ever 1 second
 console.log(icount);
}

-1

ループが実行されている間、2秒ごとにテキストを表示する単純な実装。

for (var i = 0; i < foo.length; i++) {
   setInterval(function(){ 
     console.log("I will appear every 2 seconds"); 
   }, 2000);
  break;
};

-3

これを試して

//the code will execute in 1 3 5 7 9 seconds later
function exec(){
  for(var i=0;i<5;i++){
   setTimeout(function(){
     console.log(new Date());   //It's you code
   },(i+i+1)*1000);
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.