web-dev-qa-db-ja.com

JavaScriptのループに遅延を追加する方法

whileループの中にdelay/sleepを追加したいです。

私はこれを試してみました:

alert('hi');

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

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

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

290
olidev

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
      alert('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 () {   
      alert('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
662
Daniel Vassallo

このようなことを試してください:

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

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

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

letが行うことは、ループではなく、各反復に対してiを宣言することです。このように、setTimeoutに渡されるものはまさに私たちが欲しいものです。

50
Saket Mehta

ES7以降、ループを待つより良い方法があります。

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

async function load () {
  for (var i = 0; i < 3; i++) {
    console.log(i);
    await timer(3000);
  }
}

load();

MDNに関するリファレンス

ES7が今日サポートされることはめったにないことに注意してください。ですから、どこでも使用するにはBabelと一緒に転置する必要があります。

書き出された

23
Jonas Wilms

もう1つの方法は、タイムアウトまでの時間を増やすことですが、これはがsleepとは異なることに注意してください。ループの後のコードはただちに実行され、コールバック関数の実行のみが延期されます。

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

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

21
Felix Kling

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

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
BGerrissen

私はおそらく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);
14
Abel Terefe

これはうまくいくでしょう

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

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

12
Gsvp Nagaraju

ES6(ECMAScript 2015)では、 ジェネレータ とintervalを使用して遅延を伴って反復できます。

ECMAScript 6の新機能であるジェネレータは、一時停止および再開できる機能です。 genFuncを呼び出しても実行されません。代わりに、genFuncの実行を制御することができる、いわゆるジェネレータオブジェクトを返します。 genFunc()は、最初はその本体の先頭で中断されています。 genObj.next()メソッドは、次の結果が出るまで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を使用しているのであれば、それは(私の意見では)遅れてループを達成するための最もエレガントな方法です。

7
Itay Radotzki

私は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>
4
Dave Bryand

RxJS 区間演算子 を使用できます。 Intervalはx秒ごとに整数を発行し、takeはnumberを発行する回数を指定します

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>
3
Vlad Bezden

ちょうど私がここに私の2セントを掲示すると思っただけで。この関数は遅延を伴って反復ループを実行します。 this 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);
}
3
D Slee
    var startIndex = 0;
    var data = [1, 2, 3];
    var timeout = 1000;

    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);

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
1
Jasdeep Khalsa

あなたはそれを行う:

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

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

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

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

  // 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
K48
<!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>
0
Boginaathan M

一般的な使用のために "通常のループを忘れる"そして "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)
        }

シモンズ:(setTimeOut)の本当のふるまいを理解してください:それらはすべて同じ時間に始まるでしょう「bla bla blaは同じ瞬間にカウントダウンを始めるでしょう」従って実行を調整するために異なるタイムアウトを作ります。

PS 2:タイミングループの例ですが、反応ループのためにあなたはイベントを使用することができ、非同期が約束されていると約束します..

0

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

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;
}

そして、あなたはそれを次のように実行します。

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

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

0
Questionare232
/* 
  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
*/
0
user2913925