Bagaimana cara menambahkan penundaan dalam loop JavaScript?


345

Saya ingin menambahkan penundaan / tidur di dalam satu whilelingkaran:

Saya mencobanya seperti ini:

alert('hi');

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

Hanya skenario pertama yang benar: setelah ditampilkan alert('hi'), itu akan menunggu selama 3 detik kemudian alert('hello')akan ditampilkan tetapi kemudian alert('hello')akan berulang kali terus-menerus.

Yang saya inginkan adalah setelah alert('hello')ditampilkan 3 detik setelah alert('hi')itu perlu menunggu selama 3 detik untuk kedua kalinya alert('hello')dan seterusnya.

Jawaban:


749

The setTimeout()fungsi non-blocking dan akan kembali segera. Oleh karena itu perulangan Anda akan beralih dengan sangat cepat dan akan memulai pemicu waktu 3 detik satu demi satu secara berurutan. Itulah sebabnya peringatan pertama Anda muncul setelah 3 detik, dan semua sisanya akan mengikuti secara berurutan tanpa penundaan.

Anda mungkin ingin menggunakan sesuatu seperti ini sebagai gantinya:

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

Anda juga bisa memperbaikinya, dengan menggunakan fungsi pemanggilan sendiri, meneruskan jumlah iterasi sebagai argumen:

(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


27
Tidak akan menggunakan rekursi untuk mengimplementasikan ini akan dikenakan stack overflow akhirnya? Jika Anda ingin melakukan sejuta iterasi, apa cara yang lebih baik untuk mengimplementasikannya? Mungkin setInterval dan kemudian menghapusnya, seperti solusi Abel di bawah ini?
Adam

7
@ Adam: pemahaman saya adalah, karena setTimeout adalah non-blocking, ini bukan recusion - stackwindow ditutup setelah setiap setTimeout dan hanya ada satu setTimeout yang menunggu untuk dieksekusi ... Benar?
Joe

3
Bagaimana ini akan bekerja ketika iterasi objek seperti for inloop?
vsync

1
@vsync LihatlahObject.keys()
Braden Best

1
@ joey, Anda bingung setTimeoutdengan setInterval. Timeout secara implisit dihancurkan ketika callback dipanggil.
cdhowie

72

Coba sesuatu seperti ini:

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

69

Jika menggunakan ES6, Anda dapat menggunakannya letuntuk mencapai ini:

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

Apa yang letdilakukan adalah mendeklarasikan iuntuk setiap iterasi , bukan loop. Dengan cara ini, apa yang diteruskan setTimeoutadalah apa yang kita inginkan.


1
Terima kasih! Tidak akan memikirkan metode ini sendiri. Pelingkupan blok aktual. Bayangkan itu ...
Sophia Gold

1
Saya percaya ini memiliki masalah alokasi memori yang sama dengan jawaban yang dijelaskan dalam stackoverflow.com/a/3583795/1337392
Flame_Phoenix

1
@Flame_Phoenix Apa masalah alokasi memori?
4castle

1
Panggilan setTimeout secara sinkron menghitung nilai i*3000argumen, di dalam loop, dan meneruskannya setTimeoutdengan nilai. Penggunaan letadalah opsional dan tidak terkait dengan pertanyaan dan jawaban.
traktor53

@Flame_Phoenix menyebutkan ada masalah dalam kode ini. Pada dasarnya pada pass pertama Anda membuat timer kemudian segera mengulangi loop lagi dan lagi sampai loop berakhir dengan kondisi ( i < 10) sehingga Anda akan memiliki beberapa timer bekerja secara paralel yang membuat alokasi memori dan lebih buruk pada jumlah iterasi yang lebih besar.
XCanG

63

Karena ES7 ada cara yang lebih baik untuk menunggu perulangan:

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

Ketika mesin mencapai awaitbagian, itu menetapkan batas waktu dan menghentikan eksekusiasync function . Kemudian ketika batas waktu selesai, eksekusi berlanjut pada titik itu. Itu cukup berguna karena Anda dapat menunda (1) loop bersarang, (2) kondisional, (3) fungsi bersarang:

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

Referensi tentang MDN

Sementara ES7 sekarang didukung oleh NodeJS dan peramban modern, Anda mungkin ingin mengubahnya dengan BabelJS sehingga dapat berjalan di mana-mana.


Ini bekerja dengan baik untuk saya. Saya hanya ingin bertanya apakah saya ingin memutus perulangan, bagaimana saya bisa melakukannya ketika menggunakan menunggu?
Sachin Shah

@sachin break;mungkin?
Jonas Wilms

Terima kasih atas solusi ini. Sangat menyenangkan untuk menggunakan semua struktur kontrol yang ada dan tidak perlu menemukan kelanjutan.
Gus

Ini masih akan hanya membuat berbagai timer dan mereka akan menyelesaikannya pada waktu yang berbeda daripada secara berurutan?
David Yell

@ David um tidak? Apakah Anda benar-benar menjalankan kode?
Jonas Wilms

24

Cara lain adalah memperbanyak waktu ke waktu habis, tetapi perhatikan bahwa ini tidak seperti tidur . Kode setelah loop akan dieksekusi segera, hanya eksekusi fungsi callback yang ditunda.

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

Batas waktu pertama akan diatur ke 3000 * 1, yang kedua ke 3000 * 2dan seterusnya.


2
Penting untuk ditunjukkan bahwa Anda tidak dapat diandalkan startdalam fungsi Anda menggunakan metode ini.
DBS

2
Praktik buruk - alokasi memori yang tidak perlu.
Alexander Trakhimenok

Suara positif untuk kreativitas, tapi itu praktik buruk. :)
Salivan

2
Mengapa itu praktik yang buruk, dan mengapa itu memiliki masalah alokasi memori? Apakah jawaban ini mengalami masalah yang sama? stackoverflow.com/a/36018502/1337392
Flame_Phoenix

1
@Flame_Phoenix itu praktik buruk karena program ini akan membuat satu timer untuk setiap loop, dengan semua timer berjalan pada waktu yang sama. Jadi jika ada 1000 iterasi, akan ada 1000 timer yang berjalan pada saat yang sama di awal.
Joakim

16

Ini akan bekerja

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

Coba biola ini: https://jsfiddle.net/wgdx8zqq/


1
Ini memang memicu semua panggilan timeout dekat waktu yang sama
Eddie

satu-satunya hal yang saya katakan, saya telah retak dengan cara ini, digunakan $.Deferredtetapi ada beberapa skenario yang berbeda untuk membuatnya bekerja, jempol kepada Anda ..!
ArifMustafa

15

Saya pikir Anda perlu sesuatu seperti ini:

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

Kode uji:

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

Catatan: menggunakan lansiran peringatan pelaksanaan javascript hingga Anda menutup lansiran. Mungkin lebih banyak kode daripada yang Anda minta, tetapi ini adalah solusi yang dapat digunakan kembali yang kuat.


15

Saya mungkin akan menggunakan setInteval. Seperti ini,

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 jauh lebih baik daripada settinterval. google dan Anda akan tahu
Airy

14
Saya google sekitar sedikit dan saya tidak menemukan apa pun, Mengapa setInterval buruk? Bisakah Anda memberi kami tautan? atau contoh? Terima kasih
Marcs

Saya kira intinya adalah bahwa SetInterval()terus memunculkan 'utas' bahkan jika terjadi beberapa kesalahan atau blok.
Mateen Ulhaq

8

Dalam ES6 (ECMAScript 2015) Anda dapat beralih dengan penundaan dengan generator dan interval.

Generator, fitur baru ECMAScript 6, adalah fungsi yang dapat dijeda dan dilanjutkan. Memanggil genFunc tidak menjalankannya. Sebagai gantinya, ia mengembalikan objek generator yang disebut yang memungkinkan kita mengontrol eksekusi genFunc. genFunc () awalnya ditangguhkan di awal tubuhnya. Metode genObj.next () melanjutkan eksekusi genFunc, hingga hasil berikutnya. (Menjelajahi ES6)


Contoh kode:

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

Jadi jika Anda menggunakan ES6, itu cara yang paling elegan untuk mencapai loop dengan penundaan (menurut saya).


4

Anda dapat menggunakan operator interval RxJS . Interval memancarkan integer setiap x jumlah detik, dan take menentukan berapa kali harus memancarkan angka

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

Hanya berpikir saya akan memposting dua sen saya di sini juga. Fungsi ini menjalankan perulangan berulang dengan penundaan. Lihat jsfiddle ini . Fungsinya sebagai berikut:

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

Sebagai contoh:

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

Akan sama dengan:

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

4

Saya melakukan ini dengan Bluebird Promise.delaydan rekursi.

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

Di ES6 Anda dapat melakukan hal berikut:

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

Di ES5 Anda dapat melakukan sebagai:

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

Alasannya adalah, letmemungkinkan Anda untuk mendeklarasikan variabel yang terbatas pada cakupan pernyataan blok, atau ekspresi yang digunakannya, tidak seperti varkata kunci, yang mendefinisikan variabel secara global, atau secara lokal ke seluruh fungsi tanpa memperhatikan cakupan blok.


1

Versi modifikasi dari jawaban Daniel Vassallo, dengan variabel diekstraksi ke dalam parameter untuk membuat fungsi lebih dapat digunakan kembali:

Pertama mari kita mendefinisikan beberapa variabel penting:

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

Selanjutnya Anda harus mendefinisikan fungsi yang ingin Anda jalankan. Ini akan melewati i, indeks saat ini dari loop dan panjang dari loop, jika Anda membutuhkannya:

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

Versi yang dapat dieksekusi sendiri

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

Versi fungsional

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

bagus dan bagaimana cara meneruskan data ke fungsi tanpa variabel global
Sundara Prabu

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

1

Anda melakukannya:

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>


lebih baik gunakan konsol log daripada peringatan, tidak terlalu menyenangkan untuk menutup peringatan selama setengah menit;)
Hendry

Ya. Saya melihat! Tapi permintaan waspada ... huz
Nguyen Ba Danh - FAIC HN

Mengapa mengimpor jQuery?
Elias Soares

Maaf ... tidak perlu .. heh. Saya tidak tahu memposting konten ... ini dulu.
Nguyen Ba Danh - 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
Nama fungsi Anda menghebohkan, itulah alasan utama mengapa kode ini sulit dibaca.
Mark Walters

0

Inilah cara saya membuat loop tak terbatas dengan penundaan yang terputus pada kondisi tertentu:

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

Kuncinya di sini adalah untuk membuat Janji baru yang diselesaikan dengan batas waktu, dan untuk menunggu resolusi.

Jelas Anda perlu dukungan async / menunggu untuk itu. Bekerja di Node 8.


0

untuk penggunaan umum "lupakan loop normal" dan gunakan kombinasi "setInterval" ini termasuk "setTimeOut" s: seperti ini (dari tugas sebenarnya).

        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: Memahami bahwa perilaku nyata (setTimeOut): mereka semua akan mulai dalam waktu yang sama "tiga bla bla bla akan mulai menghitung mundur pada saat yang sama" jadi buat batas waktu berbeda untuk mengatur eksekusi.

PS 2: contoh untuk timing loop, tetapi untuk loop reaksi Anda dapat menggunakan acara, janji async tunggu ..


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
Harap selalu berikan setidaknya deskripsi singkat untuk cuplikan kode Anda, setidaknya bagi orang lain untuk memastikan bahwa Anda menjawab pertanyaan itu.
Hexfire

1
Kode hanya jawaban yang tidak dianjurkan karena mereka tidak memberikan banyak informasi untuk pembaca di masa depan, silakan berikan beberapa penjelasan untuk apa yang telah Anda tulis
WhatsThePoint

0

Setahu saya setTimeoutfungsinya disebut asinkron. Yang dapat Anda lakukan adalah membungkus seluruh loop dalam fungsi async dan menunggu Promiseyang berisi setTimeout seperti yang ditunjukkan:

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

Dan kemudian Anda menyebutnya jalankan seperti ini:

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

Silakan luangkan waktu untuk mendapatkan pemahaman yang baik tentang pemrograman asinkron.


0

Coba saja ini

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

Hasil

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

-1

Berikut adalah fungsi yang saya gunakan untuk mengulang array:

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

Anda menggunakannya seperti ini:

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

-1

Skrip ini berfungsi untuk sebagian besar hal

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

Coba ini...

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

Implementasi sederhana menampilkan sepotong teks setiap dua detik selama loop berjalan.

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

-3

Coba ini

//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);
  }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.