1

Why does setTimeout not work inside a for loop? I'm trying to have a class added to each frame one at a time and every three seconds I need to remove it from the current one and add it to the next. When It gets to frame-6 I need it to wait ten seconds before removing the class. Then I need it all to repeat. But its just adding the class to them all straight away and then not removing them.

for(i = 1; i < 6; i++){
    jQuery('.frame-'+i).addClass('color');

    if(i < 6){
        setTimeout(function(){
            jQuery('.frame-'+i).removeClass('color');
        }, 3000);
    }else if(i = 6){
        setTimeout(function(){
            jQuery('.frame-'+i).removeClass('color');
            i = 1;
        }, 10000);
    }  
}
.color{
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame-1">one</div>
<div class="frame-2">two</div>
<div class="frame-3">three</div>
<div class="frame-4">four</div>
<div class="frame-5">five</div>
<div class="frame-6">six</div>
Reece
  • 2,581
  • 10
  • 42
  • 90
  • A `for` loop executes instantly, regardless of any timers in it's logic. I'd suggest you research `setInterval()` instead as it's far more appropriate for your needs. – Rory McCrossan Feb 06 '18 at 10:55
  • 2
    It does work - it's doing exactly what you've told it to do, which is to create 6 timeouts, 5 that trigger in 3 seconds and 1 that triggers in 10. If you want something to happen at 3 seconds and then 6 seconds etc. then create timeouts for those intervals instead. – Reinstate Monica Cellio Feb 06 '18 at 10:55
  • the loop doesn't wait for the timers to complete before continuing. The countdown happens separately from the moment you trigger it, in parallel to your other code. – ADyson Feb 06 '18 at 10:57
  • Possible duplicate of [Asynchronous Process inside a javascript for loop](https://stackoverflow.com/questions/11488014/asynchronous-process-inside-a-javascript-for-loop) – Liam Feb 06 '18 at 10:57
  • when function inside setTimeout executes the value of i will be 7. Your code inside setTimeout will try to remove color class from frame-7. refer https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch5.md#loops--closure – Vijay Feb 06 '18 at 11:00

1 Answers1

1

There are 2 issues with your code. Firstly, you're expecting the timeouts to execute before the next one is created, which is wrong. You're creating them all at the same time. Secondly, you're reusing the i variable inside the timeouts, so when they fire it's 6, for all of them.

However, you can make a recursive function to handle all of this, like this...

function timeoutHandler(i) {
  // if no value is passed, set i = 1
  if (!i) i = 1;
  
  // if i < 6 then create a setTimeout for 3 seconds
  // when we remove the class and call the function again with i + 1
  if (i < 6) {
    setTimeout(function() {
      $(".frame-" + i).removeClass("color");
      timeoutHandler(++i);
    }, 3000);
  }
  // else (i == 6) setTimeout for 10 seconds
  // when we remove the class and stop
  else {
    setTimeout(function() {
      $(".frame-" + i).removeClass("color");
    }, 10000);
  }
}

// add class to initialise - should really do this in html
for(i = 1; i < 7; i++) {
    $(".frame-" + i).addClass("color");
}

// start
timeoutHandler();
.color{
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame-1">one</div>
<div class="frame-2">two</div>
<div class="frame-3">three</div>
<div class="frame-4">four</div>
<div class="frame-5">five</div>
<div class="frame-6">six</div>
Reinstate Monica Cellio
  • 25,975
  • 6
  • 51
  • 67