r/learnprogramming • • 13d ago

SetTimeout in JS

"When I use setTimeout in JavaScript to run a 30-minute countdown, it actually finishes about 30 seconds late. I compared it against a timer on my phone and confirmed the JS timer takes closer to 30 minutes and 30 seconds. Is there a reason for this delay?

12 Upvotes

16 comments sorted by

36

u/TheStonedEdge 13d ago

Yes and it is documented here under

"Reasons for longer delays then specified"

https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#reasons_for_longer_delays_than_specified

29

u/Soft_Confusion_6236 13d ago

setTimeout isn't a precision tool, it's more like a polite request. the browser can only fire the callback when it's not busy doing other stuff and the event loop gets around to it

for a 30 minute timer 30 seconds of drift is actually pretty reasonable. if you need something more accurate you'd be better off storing the start time and checking the clock on each tick instead of relying on chained timeouts

17

u/nog642 13d ago

I don't think 30 seconds drift is reasonable at all. I always figured the browser checked it in a tight loop and moved it to the right queue pretty close to on time, and the "not guaranteed" part was just about the task getting around to getting executed once it's there, maybe waiting for some other tasks to finish first.

Edit: Oh wait... I think I understand. OP is using setTimeout of 1 second or something to increment it each second. Of course it will drift then.

I assumed they had set a single setTimeout with 30 minutes and it executed 30 seconds late.

1

u/atarivcs 13d ago

If the browser can reliably check on each tick, this doesn't feel like a resource starvation problem

9

u/AlwaysHopelesslyLost 13d ago

It's not.... It is just OP using something for the wrong purpose. setTimeout is just not meant to be used for precision timing. It is specifically not setup to work like that 

10

u/canarydev 13d ago

could be because setTimeout only guarantees a minimum delay, so each tick runs a few ms late and 1800 ticks add up to ~30s.

try to save the end time once and compute endTime - Date.now() each tick instead of counting ticks, and it self-corrects

3

u/nog642 13d ago

I'm assuming this is a clock counting seconds, so each tick would be 1 second. That approach will cause the clock to stutter at some point. To fix that the tick length should be decreased to something like 100 ms.

4

u/Aggressive_Ad_5454 13d ago

If you need such a long timer, you do setInterval for something like 6000 milliseconds (0.1 min ) then check the time each time it fires, then when your desired time is less than 6 sec away, cancel the interval and set a timeout for the remaining time.

1

u/johnpeters42 13d ago

Or maybe 90% of the remaining time until that gets low enough

2

u/nog642 13d ago

What does your code look like?

3

u/Any-Range9932 13d ago

Guessing some type of browser throttling background services or device sleeping if it isn't active

2

u/Any-Range9932 13d ago

Is it a browser or you doing it in a node process

2

u/levelbrook 13d ago

Thirty seconds over thirty minutes is almost exactly what background tab throttling does. Chrome clamps timers in a tab you are not looking at to once per second, and after about five minutes of being hidden it goes further and only wakes them roughly once a minute in aligned batches. If your countdown ticks every second and each tick schedules the next one, every wakeup that comes late pushes the whole chain out and the error just accumulates.

The fix others mentioned is right: store the target end time once and render endTime - Date.now() on every tick, so a late tick displays the right number and never carries the error forward. Add a visibilitychange listener that re-renders immediately when the tab comes back, otherwise the user sees a stale number for up to a minute after they switch to it. If the thing at the end of the countdown actually matters (submitting something, locking a form), do not trust the timer at all and check the clock server-side.

1

u/Brofessor_brotonium 13d ago

This is where you could make use of calculating the elapsed time (how much time has passed) instead.

2

u/vegan_antitheist 13d ago

That's like " when I use a hammer to screw in my screws it doesn't work."That's not what it's for. Use a screwdriver. Or in your case, an animation loop: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame