1

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!

3
  • I havent been able to figure it out. Sorry. Commented Aug 5, 2017 at 9:14
  • So do you really want to click the buttons 210 times, or do you just have a logical flaw when pushing to the same array you're iterating over ? Commented Aug 5, 2017 at 9:20
  • @adeneo Yes I really want the buttons to "click" 210 times. To be exact I want 20 series of presses, each time adding one more click to the previous series. It's part of the Simon game, and I haven't got to the other parts yet. Commented Aug 5, 2017 at 14:23

3 Answers 3

1

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);
});
Sign up to request clarification or add additional context in comments.

Comments

0

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.

1 Comment

Thank you! I really thought the code should stop executing while waiting for the setTimeout.
0

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>

3 Comments

Thank you! It doesn't do exactly what I hope but it's certainly helpful. I also love the way you generate the array
You're welcome. What would you like to work differently?
I wanted it to play any number of notes. But I already fixed it by adding a parameter to the function. Thanks a lot!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.