Roxanne Yang
Roxanne Yang

Reputation: 13

javascript setTimeout function doesn't work inside a loop

I'm making a Simon Game and I'm trying to make the button presses have 1 second interval. But It seems that my setTimeout function is not doing its job and all clicks are performed at once without the 1s interval. I tried alerting something outside the loop and it works just fine. Can anyone help me with this?

This is my JavaScript code:

    for (var count = 1; count <= 20; count++) {
      $("#count").html(count);
      seq.push(Math.floor(Math.random() * 4));
      seq.forEach(function(press) {
        setTimeout(function() {
          eval('$("#button' + press + '").click();');
        }, 1000);
      });
    }

and the corresponding html:

    <p>count: <span id="count">0</span></p>

    <button id="button0" onclick="sound1.play()"></button>
    <button id="button1" onclick="sound2.play()"></button>
    <button id="button2" onclick="sound3.play()"></button>
    <button id="button3" onclick="sound4.play()"></button>

Thank you!

Upvotes: 1

Views: 658

Answers (3)

trincot
trincot

Reputation: 350117

You could make an asynchronous loop, by calling a function repeatedly from within a setTimeout: that way the sequencing and delay will be as desired.

Here is a working snippet with some other ideas:

// First generate the array (only 8 to not annoy SO public):
var seq = Array.from(Array(8), _ => Math.floor(Math.random() * 4));

function replay() {
    // Iterate seq asynchronously
    (function loop(i) {
        if (i >= seq.length) return; // all done
        $("#count").text(i+1);
        $("#buttons>button").eq(seq[i]).click();
        setTimeout(loop.bind(null, i+1), 1000);
    })(0);
}

$("#buttons>button").click(function () {
    // Play sound here... 
    playNote([523, 659, 784, 880][$(this).index()], 800); 
    // Some visual effect:
    $(this).addClass("clicked");
    setTimeout($(this).removeClass.bind($(this), "clicked"), 800);
});

// Sound support
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

function playNote(frequency, duration) {
    // create Oscillator node
    var oscillator = audioCtx.createOscillator();
    oscillator.type = 'square';
    oscillator.frequency.value = frequency; // value in hertz
    oscillator.connect(audioCtx.destination);
    oscillator.start();
    setTimeout(oscillator.stop.bind(oscillator), duration);
}

// Play the sequence on page load
replay();
button {
    border: 2px solid silver;
}
button.clicked {
    border: 2px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>count (up to 8): <span id="count">0</span></p>

<div id="buttons">
    <button>A</button>
    <button>B</button>
    <button>C</button>
    <button>D</button>
</div>

Upvotes: 0

user439032
user439032

Reputation:

Your eval function is running after 1 second but all of them are.

What happens:

loop from 1 to 20
  add an item to the seq array
    loop through the seq array
      define the setTimeout to happen in 1 second.

Your code does not sleep while wating for the setTimeout to execute. So all of them are defined on the loop and happen as near as possible to the 1 second requested.

Upvotes: 0

geekonaut
geekonaut

Reputation: 5954

The problem is the way you do setTimeout.

The for loop iterates within a few milliseconds and you basically request all the clicks to run one second later, so they all happen one second later but at the same time.

If you request the first click after one, the second click after two seconds and so forth, you'll get what you want:

  seq.forEach(function(press, i) {
    setTimeout(function() {
      $("#button" + press).click();
    }, 1000 * i);
  });

Also note that you probably want to restructure your code to not do this twenty times over:

for (var count = 1; count <= 20; count++) {
  $("#count").html(count);
  seq.push(Math.floor(Math.random() * 4));
}

seq.forEach(function(press, i) {
  setTimeout(function() {
    $("#button" + press).click();
  }, 1000 * i);
});

Upvotes: 1

Related Questions