web-dev-qa-db-ja.com

JavaScript:タイムアウト付きforループ

Forループを一度に実行するのではなく、各反復後にタイムアウトを待つようにします。例えば:

for(var i=0; i<10; i++) {
    console.log(i);
    //wait for 1000
}

私はこのようなスタックオーバーフローに関する多くの解決策を見つけました:

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

ただし、すべての実装で、ループは最初に3000ミリ秒待機してから、forループ全体を一度に実行します。 1000ミリ秒待機した後に各反復が呼び出される方法はありますか。

16
Parag Gangil

あなたは簡単な数学でそれを解決することができます:

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

1000ミリ秒:0
4000ms:1
7000ms:2
10000ms:3
13000ms:4
...


コメントに従う

あなたの要求は少しぼやけているようです。最後のタイムアウト後に何かをしたい場合は、制限を設定して現在のインデックスを比較できます:

var limit = 10
for (var i=0;i<=limit;i++) {
   (function(ind) {
       setTimeout(function(){
           console.log(ind);
           if(ind === limit){
               console.log('It was the last one');
           }
       }, 1000 + (3000 * ind));
   })(i);
}

フィドル: http://jsfiddle.net/Tn4A7/


私はあなたが欲しいものを知っていると思う...

そして、それは単にすることです

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

ループ内で関数を作成しない、代わりに:

(function fiveSeconds  (n) {

  if (n < 5) setTimeout(function () {  
    fiveSeconds ( n ); // Redo if n < 5 (and pass n)
  }, 1000);
  
  console.log( n++ );

} (0)); // Initialize. n is 0

上記は、1秒間隔で0-5から10個の数字を記録します。

最新のブラウザ(およびIE10 +)

(function fiveSeconds (n) {

  console.log( n++ );

  if (n <= 5) setTimeout( fiveSeconds, 1000, n ); // Redo if n <= 5 (and pass n)
  
} (0)); // Initialize. n is 0
15
Roko C. Buljan

なぜこのようなものを使用しないのですか:

var i = 0
var id = window.setInterval(function(){
    if(i >= 10) {
        clearInterval(id);
        return;
    }

    console.log(i);
    i++;
}, 1000)
6
CannibalGorilla

これは動作します:

function initiateTimeOut(i) {
  setTimeout(function() { doStuff(i) }, 30);
}
function doStuff(i) {
    console.log(i);
    i++;
    if (i <= 10) {
        initiateTimeOut(i); 
    }
}

initiateTimeOut(0);

この方法では、関数の実行時にiのみをインクリメントします。

フィドルの例:http://jsfiddle.net/My7Zg/


または、さらに短い(http://jsfiddle.net/My7Zg/1/):

function customLoop(i) {
    console.log(i);
    i++;
    if (i<=10) {setTimeout(function(){customLoop(i);},1000);}
}
customLoop(0);
3
agconti

2つの方法で状況にアプローチできます。

  1. setTimeout()コールをさまざまな時間ですぐにスケジュールして、将来の希望する時間に実行されるようにすることができます(他の回答はその方法を示しています)。

  2. 最初の反復を実行し、次の反復をスケジュールし、目的の反復数が完了するまで次の反復の実行をスケジュールできます。これは最終的に、多くのsetTimeout()呼び出しを設定するよりもスケーラブルであり、各反復後に次に何が起こるかを制御できるため、分岐/ロジックの自由度が高まります。

より汎用的なユーティリティ関数を使用するこの2番目のオプションは、次のようになります。

// utility function to call a callback numTimes, 
// separated by delay milliseconds
function runIteration(fn, numTimes, delay) {
    var cnt = 0;
    function next() {
        // call the callback and stop iterating if it returns false
        if (fn(cnt) === false) return;
        ++cnt;
        // if not finished with desired number of iterations,
        // schedule the next iteration
        if (cnt < numTimes) {
            setTimeout(next, delay);
        }
    }
    // start first iteration
    next();

}

したがって、コンソールステートメントを実行するには、次のようにします。

runIteration(function(i) {
    console.log(i);
}, 10, 1000);

動作デモ: http://jsfiddle.net/jfriend00/HqCZ3/

これは、反復の完了時に呼び出される2番目のコールバック関数で拡張することもできます(状況によっては便利です)か、反復の完了時に解決されるプロミスを返すことができます。

約束を返すバージョンは次のようになります。 http://jsfiddle.net/jfriend00/XtJ69/

// utility function to call a callback numTimes, 
// separated by delay milliseconds
function runIteration(fn, numTimes, delay) {
    var d = $.Deferred();
    var cnt = 0;

    function end() {
        d.resolve();
    }

    function next() {
        // call the callback and stop iterating if
        // it returns false
        if (fn(cnt) === false) {
            end();
            return;
        }
        ++cnt;
        // if not finished with desired number of iterations,
        // schedule the next iteration
        if (cnt < numTimes) {
            setTimeout(next, delay);
        } else {
            end();
        }
    }
    // start first iteration
    next();
    return d.promise();
}


runIteration(function(i) {
    log(i);
}, 10, 1000).done(function() {
    log("done");
});
1
jfriend00
for (var i=0;i<=10;i++) {
   (function(ind) {
       setTimeout(function(){console.log((ind + 1)*1000, ':', ind);}, 1000 * (ind+1) );
   })(i);
}

出力:

1000 : 0
2000 : 1
3000 : 2
4000 : 3
5000 : 4
6000 : 5
7000 : 6
8000 : 7
9000 : 8
10000 : 9
11000 : 10

WORKING DEMO

1
PeterKA

私の仕事での最善の方法は、この場合「通常のループを忘れて」、「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

ここでの答えのほとんどは完全に間違っています。

各反復が終了するのを待ちたい場合--- forループを使用したくない場合---単に間違った戦略から始めてください。

カウンタとカウンタ制限を使用する必要があります。そうしないと無限にループします。

ここに解決策があります:

var optionLimit = 11;
var optionItem = 1;
function do_something_else() {
    if (optionItem < optionLimit) {
        console.log('doing stuff:' + optionItem)
        optionItem++
        dostuff();
    } else {
        console.log('no more stuff to do already reached:' + optionItem)
    }
}
function dostuff(started) {
    if (started) {
        console.log('started doing something');
    } else {
        console.log('find something else to do');
    }
    setTimeout(function () {
        do_something_else();
    }, 3000);
}
dostuff('started doing something');

インデックスを作成する必要があるアイテムのセットがある場合は、ループを使用して、次のように実行する必要があるアイテムの数をカウントできます。

var thingstodo = [
    thing1 = {
        what: 'clean room',
        time: 8000
    },
    thing2 = {
        what: 'laundry',
        time: 9000
    },
    thing3 = {
        what: 'take out trash',
        time: 6000
    },
    thing4 = {
        what: 'wash dishes',
        time: 10000
    }
]
var optionLimit = 0;
// find how many things to do from things to do list
function get_things_todo(time) {
    console.log('heres stuff i can do');
    console.log('====================');
    for (var i = 0; i < thingstodo.length; i++) {
        val = thingstodo[i];
        console.log(JSON.stringify(val.what));
        optionLimit++
    }
    setTimeout(function () {
        startdostuff(3000)
    }, time);
}
var optionItem = 0;
// find the next thing to do on the list
function get_next_thing(time) {
    setTimeout(function () {
        console.log('================================');
        console.log('let me find the next thing to do');
    }, time);
    setTimeout(function () {
        if (optionItem < optionLimit) {            
            val = thingstodo[optionItem];            
            dostuff(3000, val);
            optionItem++
        } else {
            console.log('=====================================================');
            console.log('no more stuff to do i finished everything on the list')
        }
    }, time*1.5);
}
//do stuff with a 3000ms delay
function dostuff(ftime, val) {
    setTimeout(function () {
        console.log('================================');
        console.log('im gonna ' + JSON.stringify(val.what));
        console.log('will finish in: ' + JSON.stringify(val.time) + ' milliseconds');
        setTimeout(function () {
            console.log('========');
            console.log('all done');
            get_next_thing(3000);
        }, val.time);
    }, ftime);
}
//start doing stuff
function startdostuff(time) {
    console.log('========================');
    console.log('just started doing stuff');
    setTimeout(function () {
        get_next_thing(3000);
    }, time);
}
/// get things to first
get_things_todo(3000);
0
Michael P.

これはes6 解決。次のようにブロックスコープの変数を単純に使用できる場合、関数でsetTimeoutをラップするのは本当に好きではありません。

for (let i=0; i<=10; i++) {
    setTimeout(() => {console.log(i);}, 1000 * i);
}
0
Artur Grigio

これは単純なタイムアウトのソリューションです...たぶんそれはあなたが期待するものと完全に一致しないかもしれませんが、javascriptで「一時停止」しようとすることは私のアドバイスでは良いアプローチではありません。あなたが望むことをするために他の方法を検索することをお勧めします。 フィドル

window.my_condition = true;
window.my_i = 0;

function interate() {
    console.log(window.my_i);
    // ... your code
    if (window.my_condition!==false) {
        window.my_i++;
        setTimeout(interate,300);
    }
}

interate();
0
weeger