Bir JavaScript döngüsüne nasıl gecikme eklerim?


346

Bir whiledöngü içinde bir gecikme / uyku eklemek istiyorum :

Ben böyle denedim:

alert('hi');

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

Sadece ilk senaryo doğrudur: gösterdikten sonra alert('hi')3 saniye bekleyecek, sonra alert('hello')görüntülenecek, ancak alert('hello')sürekli olarak tekrarlanacaktır.

Ne istiyorum sonra alert('hello')3 saniye sonra gösterilir sonra alert('hi')ikinci kez 3 saniye beklemek gerekir alert('hello')ve böylece.

Yanıtlar:


750

setTimeout()Fonksiyonu olmayan engelleme ve hemen geri döner. Bu nedenle döngünüz çok hızlı bir şekilde tekrarlanır ve hızlı bir şekilde art arda 3 saniyelik zaman aşımı tetikleyicileri başlatır. Bu yüzden ilk uyarılarınız 3 saniye sonra açılır ve geri kalan her şey gecikmeden ardarda takip edilir.

Bunun yerine böyle bir şey kullanmak isteyebilirsiniz:

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

Ayrıca, kendini çağırma işlevini kullanarak, yineleme sayısını bir argüman olarak ileterek de düzenleyebilirsiniz:

(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
Bunu uygulamak için özyineleme kullanmak nihayetinde yığın taşmasına maruz kalmaz mı? Milyonlarca yineleme yapmak isterseniz, bunu uygulamanın daha iyi bir yolu ne olurdu? Belki setel'i ayarlayın ve sonra Abel'in aşağıdaki çözümü gibi temizleyin?
Adam

7
@Adam: Benim anlayışım, setTimeout engellemediğinden, bu geri dönüş değil - yığın kümesi her setTimeout'tan sonra kapanıyor ve yalnızca bir setTimeout'un yürütülmesini bekliyor ... Değil mi?
Joe

3
for inDöngü gibi bir nesneyi yinelediğinde bu nasıl çalışır ?
vsync

1
@vsync BakınObject.keys()
Braden Best

1
@joey Sen karıştırıyorsun setTimeoutile setInterval. Geri arama çağrıldığında zaman aşımları dolaylı olarak yok edilir.
cdhowie

72

Bunun gibi bir şey deneyin:

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

69

ES6 kullanıyorsanız, bunu letbaşarmak için kullanabilirsiniz :

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

Ne letyapar beyan olan iher biri için yineleme değil, döngü. Bu şekilde, aktardığımız setTimeoutşey tam olarak istediğimiz şeydir.


1
Teşekkür! Bu yöntemi kendi başıma düşünemezdim. Gerçek blok kapsam belirleme. Hayal edin ...
Sophia Gold


1
@Flame_Phoenix Hangi bellek ayırma sorunları?
4castle

1
SetTimeout çağrısı i*3000, döngü içinde bağımsız değişkenin değerini senkronize olarak hesaplar ve değere göre iletir setTimeout. Kullanımı letisteğe bağlıdır ve soru ve cevap ile ilgisizdir.
traktor53

@ Alev_Phoenix bu kodda sorunlar olduğunu belirtti. Temelde ilk geçişte zamanlayıcı oluşturursunuz, daha sonra döngü koşuluna göre döngü sonuna kadar tekrar tekrar döngüyü tekrarlarsınız ( i < 10), böylece bellek tahsisi oluşturan birden fazla zamanlayıcıya sahip olursunuz ve daha büyük miktarlarda tekrarlanır.
XCanG

63

ES7 için daha iyi bir şekilde var yana bekliyor bir döngü:

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

Motor awaitparçaya ulaştığında, bir zaman aşımı belirler ve çalışmasını durdururasync function . Sonra zaman aşımı tamamlandığında, yürütme bu noktada devam eder. Bu, (1) iç içe döngüler, (2) koşullu, (3) iç içe işlevleri geciktirebileceğiniz için oldukça kullanışlıdır:

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 ile ilgili referans

ES7 artık NodeJS ve modern tarayıcılar tarafından desteklenirken, her yerde çalışması için BabelJS ile aktarmak isteyebilirsiniz .


Benim için iyi çalışıyor. Ben sadece döngü kırmak istiyorsanız, beklerken kullanırken nasıl yapabilirim diye sormak istiyorum.
Sachin Shah

@sachin break;belki?
Jonas Wilms

Bu çözüm için teşekkürler. Mevcut tüm kontrol yapılarını kullanmak güzeldir ve devamları icat etmek gerekmez.
Gus

Bu yine de çeşitli zamanlayıcılar yaratır ve sırayla değil, farklı zamanlarda çözülür mü?
David Yell

@JonasWilms 'Snippet'i Çalıştır' düğmesini tamamen kaçırdığım anlaşılıyor: facepalm:
David Yell

24

Başka bir yol, zaman aşımı için zamanı çoğaltmaktır, ancak bunun uyku gibi olmadığını unutmayın . Döngüden sonraki kod hemen yürütülür, yalnızca geri arama işlevinin yürütülmesi ertelenir.

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

İlk zaman aşımı 3000 * 1, ikincisi 3000 * 2vb. Olarak ayarlanır .


2
startBu yöntemi kullanarak işlevinizin içinde güvenle kullanamayacağınızı belirtmek gerekir.
DBS

2
Kötü uygulama - gereksiz bellek tahsisi.
Alexander Trakhimenok

Yaratıcılık için oy verin, ancak bu kötü bir uygulamadır. :)
Salivan

2
Neden kötü bir uygulama ve neden bellek ayırma sorunları var? Bu cevap aynı sorunları yaşıyor mu? stackoverflow.com/a/36018502/1337392
Flame_Phoenix

1
@Flame_Phoenix Kötü bir uygulama çünkü program her döngü için bir zamanlayıcı tutacak ve tüm zamanlayıcılar aynı anda çalışacak. 1000 yineleme varsa, başlangıçta aynı anda çalışan 1000 zamanlayıcı olacaktır.
Joakim

16

Bu çalışacak

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

Bu kemanı deneyin: https://jsfiddle.net/wgdx8zqq/


1
Bu aynı zamanda tüm zaman aşımı çağrılarını tetikler
Eddie

Dediğim tek şey, bu şekilde çatladım, kullandım $.Deferredama çalışmasına izin vermek için farklı bir senaryo vardı, başparmak sana ..!
ArifMustafa

15

Bence böyle bir şeye ihtiyacın var:

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

Test kodu:

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

Not: uyarıları kullanmak, uyarıyı kapatana kadar javascript yürütmeyi durdurur. İstediğinizden daha fazla kod olabilir, ancak bu yeniden kullanılabilir sağlam bir çözümdür.


15

Muhtemelen kullanırdım setInteval. Bunun gibi,

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, yerleşik değerden çok daha iyidir. google ve bileceksiniz
Airy

14
Ben biraz google ve hiçbir şey bulunamadı, neden setInterval kötü? Bize bir link verebilir misiniz? ya da bir örnek? Teşekkürler
Marcs

Bazı nokta veya hata durumunda bile 'iş parçacığı' yumurtlama tutar nokta oldu sanırım SetInterval().
Mateen Ulhaq

8

ES6'da (ECMAScript 2015) jeneratörle gecikmeli olarak yineleyebilirsiniz ve aralık .

ECMAScript 6'nın yeni bir özelliği olan jeneratörler, duraklatılabilen ve devam ettirilebilen işlevlerdir. GenFunc çağrıldığında yürütmez. Bunun yerine, genFunc'un yürütülmesini kontrol etmemizi sağlayan bir jeneratör nesnesi döndürür. genFunc () başlangıçta gövdesinin başında askıya alınır. GenObj.next () yöntemi, bir sonraki verime kadar genFunc'un yürütülmesine devam eder. (ES6'yı Keşfetme)


Kod örneği:

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

Yani ES6 kullanıyorsanız, gecikme ile döngü elde etmenin en zarif yolu (benim görüşüme göre).


4

RxJS aralık operatörünü kullanabilirsiniz . Aralık, her x saniyede bir tamsayı yayınlar ve alma, sayıların kaç kez yayılması gerektiğini belirtir.

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

İki sentimi de buraya göndereceğimi düşündüm. Bu işlev gecikmeli yinelemeli bir döngü çalıştırır. Bu jsfiddle bakın . Fonksiyon aşağıdaki gibidir:

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

Örneğin:

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

Şuna eşdeğer olur:

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

4

Bunu Bluebird Promise.delayve özyineleme ile yapıyorum .

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'da aşağıdakileri yapabilirsiniz:

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

ES5'te şunları yapabilirsiniz:

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

Bunun nedeni, bir değişkeni global olarak tanımlayan anahtar kelimenin letaksine, varblok kapsamından bağımsız olarak bir fonksiyonun tamamını tanımlayan anahtar ifadenin aksine, bir blok ifadesinin kapsamı veya üzerinde kullanıldığı ifadeyle sınırlı değişkenler bildirmenize izin vermesidir .


1

Daniel Vassallo'nun cevabının değiştirilmiş bir versiyonu, değişkenler fonksiyonu tekrar kullanılabilir hale getirmek için parametrelere çıkarıldı:

İlk olarak bazı temel değişkenleri tanımlayalım:

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

Ardından, çalıştırmak istediğiniz işlevi tanımlamanız gerekir. İhtiyacınız olması durumunda i, döngünün geçerli indeksi ve döngünün uzunluğu geçecektir:

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

Kendi kendine çalışan sürüm

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

Fonksiyonel versiyonu

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

güzel ve küresel bir değişken olmadan işleve nasıl veri aktarabilirim
Sundara Prabu

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

1

Sen yap:

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>


uyarılar yerine konsol günlüklerini daha iyi kullanın, uyarıları yarım dakika kapatmak için çok eğlenceli değildi;)
Hendry

Evet. Anlıyorum! Ama istek uyarı ... huz
Nguyen Ba Danh - FAIC HN

Neden jQuery içe aktarmalı?
Elias Soares

Üzgünüm ... gereksiz .. heh. Gönderi içeriğini bilmiyorum ... önce bunu.
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
İşlev adlarınız çok korkunç, bu kodun okunmasının bu kadar zor olmasının ana nedeni bu.
Mark Walters

0

İşte nasıl belirli bir durumda kırılan bir gecikme ile sonsuz bir döngü oluşturdu:

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

Buradaki anahtar, zaman aşımı ile çözülen yeni bir Söz oluşturmak ve çözümlenmesini beklemektir.

Açıkçası bunun için asenkron / destek beklemeniz gerekiyor. Düğüm 8'de çalışır.


0

ortak kullanım için "normal döngüler unut" ve "setInterval" bu kombinasyonu kullanın "setTimeOut" s içerir: bunun gibi (benim gerçek görevleri).

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

Not: (setTimeOut) 'un gerçek davranışının hepsinin aynı anda başlayacağını anlayın "üç bla bla bla aynı anda geri saymaya başlayacaktır", bu yüzden yürütmeyi düzenlemek için farklı bir zaman aşımı yapın.

PS 2: zamanlama döngüsü için örnek, ancak bir reaksiyon döngüleri için olayları kullanabilirsiniz, söz asenkron bekliyorum ..


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
Lütfen kod snippet'lerinize her zaman en azından kısa bir açıklama verin, en azından başkalarının soruyu ele aldığınızdan emin olması için.
Hexfire

1
Kod sadece cevaplar öyle cesaret
vermez

0

Bildiğim setTimeoutkadarıyla fonksiyon asenkron olarak adlandırılıyor. Yapabileceğiniz şey, tüm döngüyü bir eşzamansız işlev içine sarmak Promiseve setTimeout'u gösterildiği gibi bir beklemek :

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

Ve sonra şöyle çalıştırın:

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

Zaman uyumsuz programlamayı daha iyi anlamak için lütfen biraz zaman ayırın.


0

Sadece şunu dene

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

Sonuç

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

-1

İşte bir dizi üzerinden döngü için kullandığım bir işlev:

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

Bu şekilde kullanırsınız:

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

-1

Bu senaryo çoğu şey için çalışır

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

Bunu dene...

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

Döngü çalıştığı sürece bir metin parçasını iki saniyede bir göstermenin kolay uygulanması.

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

-3

Bunu dene

//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);
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.