0

I have created an infinite loop to check if a certain time is met and change my interface accordingly.

function infiniteloop(){
    let now = moment();
    start = moment(myStartTime);
    end = moment(myEndTime);
    if (now.isBetween(start, end)) {
        console.log('This is happening now')
    } else  {
        console.log('This is not happening now')
    }
    setTimeout(infiniteloop, 5000);
}

infiniteloop() is called when my page renders. As the variables myStartTimsand myEndTimemay change, it is necessary to call that function again when they change. But then it runs multiple times.

So I tried to call it with

clearTimeout(infiniteloop);
infiniteloop();

But that doesn't do the Trick. How can I ensure the loop is stopped and called again?

Torf
  • 1,154
  • 11
  • 29
  • 1
    "*the variables myStartTimsand myEndTimemay change, it is necessary to call that function again when they change.*" only if you expect the start/end to move the event from *not* now to *now* (or vice versa) and you don't want to wait 5 seconds. Is that the case? Especially the last part - is it required to re-run *immediately* rather than up to 5 seconds later? Note that you can also simply reduce the delay from 5 seconds to, say, 1 second. – VLAZ Mar 20 '23 at 15:00
  • 3
    `clearTimeout(infiniteloop);` here you are calling `clearTimeout` using a function name as the argument, when `clearTimeout` should take an id returned from `setTimeout` as the argument. https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout – Ben Stephens Mar 20 '23 at 15:14
  • 2
    [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout) expects a timeout id, the return of [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout), as a parameter and not a function. – Lain Mar 20 '23 at 15:14
  • Does this answer your question? [Stop setInterval call in JavaScript](https://stackoverflow.com/questions/109086/stop-setinterval-call-in-javascript) – Heretic Monkey Mar 20 '23 at 16:54

2 Answers2

2

Here's a setRepeating that is subtly different from setInterval. A setInterval call will do something at regular intervals regardless of how long the function itself takes. Whereas this setRepeating call will run the function, then after the function is done, it will wait for the full interval period before running it again.

It's the difference between baking a cake, then waiting one minute, then baking another cake, vs. trying to bake one cake every minute.

It's also functionally different from setInterval in that this function returns a cancellation function instead of an id, which is a little cleaner, IMO. Because the only thing you ever do with a timer id is cancel the timer.

To "restart" this, just call setRepeating again and get a new cancel function.

const setRepeating = (fn, interval) => {
    let timer;
    const scheduleNext = () => {
        timer = setTimeout(() => {
            fn();
            scheduleNext();
        }, interval);
    }
    scheduleNext();
    return () => {
        clearTimeout(timer);
    }
}

// And here's an example of how to use it:
// Log the date, then, one second later, do it again.
cancel = setRepeating(() => console.log(new Date()), 1000);

// as an example, cancel it after 5 seconds
setTimeout(() => cancel(), 5000);
Wyck
  • 10,311
  • 6
  • 39
  • 60
  • I'm mainly here to learn and I really struggled to understand how it works. If I finally understood then 1) if the function fn() takes a long time to complete (>interval) can we have simultaneous executions? 2) Could fn() return a value to stop the iteration eg: if (fn()) scheduleNext(); to have two ways to stop the loop? Have I understood correctly? – ΑΓΡΙΑ ΠΕΣΤΡΟΦΑ Mar 20 '23 at 18:37
  • @ΑΓΡΙΑΠΕΣΤΡΟΦΑ, essentially it's calling `setTimeout` - which triggers exactly once after the time has elapsed. Every time the timer elapses, it calls the function and then schedules another timeout. Each time a new timeout is set, it replaces `timer` with a new id (much like you did in your example). That's about it. The only other interesting thing is to return a cancellation function that calls `clearTimeout` with the most recent timer id. – Wyck Mar 20 '23 at 18:43
  • Hi @Wyck!, Since you didn't answer me if I understood correctly, I looked it up myself and came up with this: As it is, it's not guaranteed to repeat in interval time because fn() can take longer than interval. Therefore, to have constant repetition, scheduleNext() must be executed first and then fn(). (Then of course we can have simultaneous execution) Do you agree? – ΑΓΡΙΑ ΠΕΣΤΡΟΦΑ Mar 20 '23 at 20:08
  • @ΑΓΡΙΑΠΕΣΤΡΟΦΑ, Strictly speaking, no. constant repetition cannot be achieved just by calling `scheduleNext` first. It will be close, but there will still be a slight drift as the amount of time that passes between the signalling of the event and the execution of scheduleNext is up to the operating system's scheduler and how busy the system is at that moment. Constant repetition is achieved with the function `setInterval`. – Wyck Mar 21 '23 at 13:03
1
let timeoutId

function infiniteloop(){
   let now = moment();
   start = moment(myStartTime);
   end = moment(myEndTime);
   if (now.isBetween(start, end)) {
       console.log('This is happening now')
   } else  {
       console.log('This is not happening now')
   }
   timeoutId = setTimeout(infiniteloop, 5000);
}

infiniteloop()
.....
myStartTime = anytime1
myEndTime = anytime2
// when change the myStartTime or myEndTime do:
.....

clearTimeout(timeoutId)
infiniteloop()
  • This is only capable of stopping the first setTimeout. Once it triggers, and the next infiniteloop is scheduled, that id is no longer in use, but a new one has been created and lost because the return value of infiniteloop is discarded when passed to setTimeout. – Wyck Mar 20 '23 at 17:43
  • 1
    Xmm... yes, maybe set the equation inside the infiniteloop to have always a fresh timeoutId. Is ok this way? – ΑΓΡΙΑ ΠΕΣΤΡΟΦΑ Mar 20 '23 at 17:46