The default behaviour of setTimeout seems problematic. Could be used for an exploit, because code like this might not work as expected:<p><pre><code> const attackerControlled = ...;
if (attackerControlled < 60_000) {
throw new Error("Must wait at least 1min!");
}
setTimeout(() => {
console.log("Surely at least 1min has passed!");
}, attackerControlled);
</code></pre>
The attacker could set the value to a comically large number and the callback would execute immediately. This also seems to be true for NaN. The better solution (imo) would be to throw an error, but I assume we can't due to backwards compatibility.
This type of thing is actually practical. Google Cloud Tasks have a max schedule date of 30 days in the future so the typical workaround is to chain tasks. As other commenters have suggested you can also set a cron check. This has more persistent implications on your database, but chaining tasks can fail in other ways, or explode if there are retries and a failed request does trigger a reschedule (I hate to say I’m speaking from experience)
If we're pedantic, this doesn't actually do what's advertised, this would be waiting X timeouts worth of event cycles rather than just the one for a true Big timeout, assuming the precision matters when you're stalling a function for 40 days.
In response to this, I read the spec of setTimeout, bu I couldn't find the part where implementations may have an upper bound. Can someone more familiär with the specs point me in the right direction?
Sounds a lot like the famous windows 95 bug when it would crash after 49.7 days of uptime [1]<p>[1] <a href="https://news.ycombinator.com/item?id=28340101">https://news.ycombinator.com/item?id=28340101</a>
> In most JavaScript runtimes, this duration is represented as a 32-bit signed integer<p>I thought all numbers in JavaScript were basically some variation of double precision floating points, if so, why is setTimeout limited to a smaller 32bit signed integer?<p>If this is true, then if I pass something like "0.5", does it round the number when casting it to an integer? Or does it execute the callback after half a millisecond like you would expect it would?
Interestingly, this library seems to suffer from the opposite problem: where setTimeout can trigger earlier than expected, setBigTimeout can trigger never at all!<p>The problem is that when setBigTimeout is invoked with a floating-point number (and numbers are floating-point in JS by default), it keeps computing the time left till trigger in floating point. But FP numbers are weird:<p><pre><code> > 1e16 - 1 == 1e16
true
</code></pre>
At some point, they don't have enough precision to represent exact differences, so they start rounding, and this gets extremely more inaccurate as the value increases. For correct behavior, remainingDelay needs to be stored in BigInt.<p>Of course, this problem is mostly theoretical, as it starts happening at around 2^83 milliseconds, which doesn't even fit in a 64-bit time_t, and it's not like humanity will exist by then. But still!
Awesome! Already have a project I can use this on, thanks.<p>As a side note, why do you use this weird non-Github, non-Gitlab, non-Bitbucket sketchy looking git host? I can see the code obviously, but it makes me worry about supply chain security.
instead of chaining together shorter timeouts, why not calculate the datetime of the delay and then invoke via window.requestAnimationFrame (by checking the current date ofc).
setTimeout is stranger than you think.<p>We recently had a failed unit test because setTimeout(fn, 1000) triggered at 999ms. That test had ran more than a hundred times before just fine. Till one day it didn't.
This makes me love having Go handy. I find working with signals and time based events so much nicer than other languages I use.<p>This is fun, though. JS is a bucket of weird little details like this.
This is excellent. But I was hoping for a setTimeout that survived JavaScript environment restarts. Maybe <i>setBigReliableTimeout</i> is in your future? Hahaha! :)