简体   繁体   中英

How can I stop nested setTimeout with clearTimeout?

I have a function everyXsecsForYsecs that will accept three arguments: a function, an interval time in seconds, and a total time in seconds.

everyXsecsForYsecs should invoke the given function every X * 1000 milliseconds, yet then stop invoking the function after Y * 1000 milliseconds. Addition to this, here is a simple callback function for everyXsecsForYsecs :

function sayHowdy(){
  console.log('Howdy');
}

Then I call everyXsecsForYsecs as such:

everyXsecsForYsecs(sayHowdy, 1, 5);

So what I expect is to see 5 'Howdy' in the console, then function to stop. But what happens is that, function prints 'Howdy' for ever. Here is how I implemented everyXsecsForYsecs ,

function everyXsecsForYsecs(callback, X, Y) {
  x_mili = X * 1000;
  y_mili = Y * 1000;

let id = setTimeout(function run(){
    callback();
    id = setTimeout(run, x_mili);
  }, x_mili);
setTimeout(clearTimeout, y_mili, id);
}

I am suspicious about how I use clearTimeout with nested setTimeout , What I am missing exactly?

By the time

 setTimeout(clearTimeout, y_mili, id);

runs, id contains the timer id of the first outer setTimeout call . Cancelling that won't really help. If you'd replace it with:

 setTimeout(() => clearTimeout(id),  y_mili);

it'll clear the timeout with the id at that time , as you evaluate id when the timeout is done , and not when it get's started .


I'd write it as:

 function everyXsecsForYsecs(callback, X, Y) {
   let count = Math.floor(Y / X);
   function recurring() {
      callback();
      if((count -= 1) >= 0)
        setTimeout(recurring, X * 1000);
  }
  setTimeout(recurring, X * 1000);
 }
let firstId = setTimeout(sayHowdy, 1000)

will call sayHowdy after 1000ms and store the timeout id within firstId

clearTimeout(firstId)

if this is called, the timeout referenced by the id will be cleared (no matter if it already is over or not)

But the question actually is, why you would want to clear the timeout, it's no interval, so you probably are in the wrong box.

have a look at this snippet, it does not repeat for seconds, but x times with recursion:

function fireAllXSecsYTimes(callback, fireAfterSeconds, counter) {
  if (counter === 0) {
    return;
  }
  setTimeout(() => {
    callback();
    counter--;
    fireAllXSecsYTimes(callback, fireAfterSeconds, counter);
  }, fireAfterSeconds * 1000)
}

what you asked for:

function fireAllXSecsForYSecs(callback, fireAfterSeconds, remainingSeconds) {
  if (remainingSeconds <= 0) {
    return;
  }
  setTimeout(() => {
    callback();
    fireAllXSecsForYSecs(callback, fireAfterSeconds, remainingSeconds - fireAfterSeconds);
  }, fireAfterSeconds * 1000)
}

called with fireAllXSecsForYSecs(() => console.log('howdy'), 2, 5) it will log 'howdy' 3 times, because on third execution, remainingSeconds still has 1 left. If you want to prevent this, just return if remainingSeconds <= 0 || remainingSeconds < fireAfterSeconds remainingSeconds <= 0 || remainingSeconds < fireAfterSeconds

Pass the reference not the value.

 function sayHowdy() { console.log('Howdy'); } function everyXsecsForYsecs(callback, X, Y) { x_mili = X * 1000; y_mili = Y * 1000; let id = setTimeout(function run() { callback(); id = setTimeout(run, x_mili); }, x_mili); setTimeout(() => clearTimeout(id), y_mili); //here you need to pass the reference to the id not the value //which is constantly changing } everyXsecsForYsecs(sayHowdy, 1, 5);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM