javascriptanimationsettimeoutsetintervalcleartimeout

How to stop the setTimeout or setInterval inside loop?


I'd gone through a plenty of posts in SO with the similar queries but unfortunately none of them fit into my requirements or solved my problem.

The problem is When I click play button, I want to go through all the iteration(102) of while with certain delay(for visualisation purpose) until I click the pause button. If the pause button is clicked at iteration 73 execution, I want to stop at the current iteration(73) and save this step. Later, If the play button is pressed, I want to resume from the iteration(73/74) from where I left off. Variable curStp is used to monitor the current steps.

Currently, when the pause button is pressed, the loop is not stopping and it keeps going till it reaches 102.

let flag = 0;
const delay = 300;
const totalStps = 102;
var curStp = 0;

function mouseup() {
  let i = 0;
  console.log("Value of flag " + flag);
  while(i < totalStps - curStp) {
    const j = i;
    var timeout = setTimeout(function(){
       let stp = curStp;
       console.log("i " + i + "  j " + j + " curStp " + curStp);
       
       curStp = stp+1;   // this is done by setState.
       console.log("flag " + flag + " timeout " + timeout);
        }, delay * i);
        
    if (flag === 1) {
       console.log("break the loop");
       clearTimeout(timeout);
       // This is not stopping this setTimeout
       break;
    }
    i++;
  } 
}

function pause() {
  flag = 1;
  console.log("Value of flag in pause()  " + flag + " curStp " + curStp);
  let stp = curStp;
  curStp = stp;   // this is done by setState.
}
<button onclick="mouseup()">Run Code
</button>
<button onclick="pause()">Pause Code
</button>

I tried the same with setInterval also but no luck. It also runs in a jiffy and hurting my eyes.

let flag = 0;
const delay = 300;
const totalStps = 102;
var curStp = 0;

function mouseup() {
  let i = 0;
  console.log("Value of flag " + flag);
  while(i < totalStps - curStp) {
    const j = i;
    (function(i) {
    var timeout = setInterval(function(){
       let stp = curStp;
       console.log("i " + i + "  j " + j + " curStp " + curStp);
       
       curStp = stp+1;   // this is done by setState.
       console.log("flag " + flag + " timeout " + timeout);
        }, delay * i)
    })(i);
    if (flag === 1) {
       console.log("break the loop");
       clearInterval(timeout);
       // This is not stopping this setTimeout
       break;
    }
    i++;
  } 
}

function pause() {
  flag = 1;
  console.log("Value of flag in pause()  " + flag + " curStp " + curStp);
  let stp = curStp;
  curStp = stp;   // this is done by setState.
}
<button onclick="mouseup()">Run Code
</button>
<button onclick="pause()">Pause Code
</button>

Am I missing any thing?


Solution

  • How about you do it this way:

    1. Don't use a while loop because the while loop will call setTimeout many times and then you will have to clear ALL of them to pause. So it is simpler to not use the while loop
    2. When the function is called, let it set a timeout again after it is run.
    3. This means when toRepeat in my example below runs, it will create a timeout to run again so it runs the next step. Then it runs again and calls setTimeout again and so on until curStp is less than totalStps

    Here is an example with comments, let me know if anything needs clarification.

    let flag = 0;
    const delay = 300;
    const totalStps = 102;
    var curStp = 0;
    var timeout = null;
    
    function toRepeat() {
        let stp = curStp;
        console.log("curStp = " + curStp);
        
        curStp = stp+1;   // this is done by setState.
        console.log("flag " + flag + " timeout " + timeout);
    
        // check for pause
        if(flag === 1) {
          console.log("break the loop");
          console.log('clearing timeout = ', timeout);
    
          // dont continue because we paused
          return;
        }
    
        // each timeout will call another timeout again for the next step unless we already finished or we have paused
        // better to have this `curStp < totalStps`
        if(curStp != totalStps) {
          // setup another timeout for next step
          timeout = setTimeout(toRepeat, delay);
        } else {
          // we already finished
        }
    }
    
    function mouseup() {
      // need to set the flag back to 0 so pressing "Run Code" doesn't immediately pause again
      flag = 0;
    
      var timeout = setTimeout(toRepeat, delay);
    }
    
    function pause() {
      flag = 1;
      console.log("Value of flag in pause()  " + flag + " curStp " + curStp);
      let stp = curStp;
      curStp = stp;   // this is done by setState.
    }