9 ms·
setBigTimeout
- miiiiiike 2y agoGot hit with this one a few months ago.
- graypegg 2y agoJust out of curiosity, what was the use case for a really long timeout? Feels like most if not all long timeouts would be best served with some sort of "job" you could persist, rather than leaving it in the event queue.
- miiiiiike 2y agoNice to have bit of client side auth code. A timer is set to update your auth status when the refresh token is scheduled to expired. I've left tabs open for 30+ days.
- cout 2y agohttps://thedailywtf.com/articles/The_Harbinger_of_the_Epoch_ https://thedailywtf.com/articles/The_Harbinger_of_the_Epoch_
- graypegg 2y agoTo be fair, this will be fixed by browsers when it's within spitting distance of the scale of numbers setTimeout is normally used with. (not huge numbers) Like, if it's close enough that setTimeout(() => {}, 5000) will stop working a month later, that would be a major failure on the browser vendor's part. Much too close for comfort. But I totally understand it not being a priority if the situation is: setTimeout(() => {}, 500000000) not working in X years.
- BillyTheKing 2y agothis is the thing with JS and TS - the types and stuff, it's all good until you realise that all integers are basically int 52 (represented as float 64, with 52 bits for the fraction). Yes, it's nice and flexible - but also introduces some dangerous subtle bugs.
- 8n4vidtmkvmk 2y ago2^53-1 I thought. And no, they're not all that. There's a bunch that are 2^32 such as this timeout, apparently, plus all the bit shift operations.
- vhcr 2y agoNot ALL integers are 52 bit, BigInts were added on ECMAScript 2020.
- sjaak 2y agoWhat is the use-case for such a function?
- echoangle 2y agoMake a joke and have something to write a blogpost about, while letting your readers learn something new.
- keithwhor 2y agoOff the top of my head, a cron scheduler for a server that reads from a database and sets a timeout upon boot. Every time the server is reboot the timeouts are reinitialized (fail safe in case of downtime). If upon boot there’s a timeout > 25 days it’ll get executed immediately which is not the behavior you want.
- skykooler 2y agoWhy would you do that in JS rather than just using cron for it?
- efilife 2y agoIt can be quicker since you are in the environment already and you are sure that they will activate only when your program is running
- hinkley 2y agoThis should be an interval with a lookup. Every five seconds check for due dates sooner than 10 seconds from now and schedule them. The longer a delay the higher the odds the process exits without finishing the work.
- bgirard 2y agoNot having your timeout fire unexpectedly instantly is a good use-case IMO.
- yifanl 2y agoIf 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.
- keithwhor 2y agoI haven’t looked at the code but it’s fairly likely the author considered this? eg the new timeout is set based on the delta of Date.now() instead of just subtracting the time from the previous timeout.
- yifanl 2y agoNo, it pretty much just does exactly that. const subtractNextDelay = () => { if (typeof remainingDelay === "number") { remainingDelay -= MAX_REAL_DELAY; } else { remainingDelay -= BigInt(MAX_REAL_DELAY); } };
- keithwhor 2y agoOh yikes. Yeah; not ideal.
- Aachen 2y agoTo be fair, this is what I expect of any delay function. If it needs to be precise to the millisecond, especially when scheduled hours or days ahead, I'd default to doing a sleep until shortly before (ballpark: 98% of the full time span) and then a smaller sleep for the remaining time, or even a busy wait for the last bit if it needs to be sub-millisecond accurate I've had too many sleep functions not work as they should to still rely on this, especially on mobile devices and webpages where background power consumption is a concern. It doesn't excuse new bad implementations but it's also not exactly surprising
- keepamovin 2y ago
- n2d4 2y agoThe default behaviour of setTimeout seems problematic. Could be used for an exploit, because code like this might not work as expected: const attackerControlled = ...; if (attackerControlled < 60_000) { throw new Error("Must wait at least 1min!"); } setTimeout(() => { console.log("Surely at least 1min has passed!"); }, attackerControlled); 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.
- arghwhat 2y agoA scenario where an attacker can control a timeout where having the callback run sooner than one minute later would lead to security failures, but having it set to run days later is perfectly fine and so no upper bound check is required seems… quite a constructed edge case. The problem here is having an attacker control a security sensitive timer in the first place.
- a_cardboard_box 2y agoThe exploit could be a DoS attack. I don't think it's that contrived to have a service that runs an expensive operation at a fixed rate, controlled by the user, limited to 1 operation per minute.
- lucideer 2y ago> I don't think it's that contrived to have a service that runs an expensive operation at a fixed rate, controlled by the user Maybe not contrived but definitely insecure by definition. Allowing user control of rates is definitely useful & a power devs will need to grant but it should never be direct control.
- shawnz 2y agoCan you elaborate on what indirect control would look like in your opinion? No matter how many layers of abstraction you put in between, you're still eventually going to be passing a value to the setTimeout function that was computed based on something the user inputted, right? If you're not aware of these caveats about extremely high timeout values, how do any layers of abstraction in between help you prevent this? As far as I can see, the only prevention is knowing about the caveats and specifically adding validation for them.
- issafram 2y agoI wish that I could actually see the code. I understand that it's chaining timeouts, but the git site is just garbage
- maxbond 2y agoYou've gotta click "tree". https://git.sr.ht/~evanhahn/setBigTimeout/tree/main/item/mod.ts https://git.sr.ht/~evanhahn/setBigTimeout/tree/main/item/mod...
- zgk7iqea 2y agoyes, sourcehuts interface is just godawful
- egwynn 2y agoI agree it’s not the prettiest, but I had no trouble clicking on “tree” to get to the folder and then “mod.ts” to see the code.
- Joker_vD 2y agoOne has still to know that "tree" stands for "source code".
- internetter 2y agoThis is not a sourcehut problem, it is a github problem. "Tree" is semantically correct.
- yesco 2y ago> In most JavaScript runtimes, this duration is represented as a 32-bit signed integer 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? 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?
- arp242 2y agoYou're correct about JS numbers. It works like this presumably because the implementation is written in C++ or the like and uses an int32 for this, because "25 days ought to be enough for everyone".
- drdaeman 2y agoI thought most non-abandoned C/C++ projects have long switched to time_t or similar. 2038 is not that far in the future.
- bobmcnamara 2y agoDebian conversion should be done mid2025.
- andrewmcwatters 2y ago2038 is even "now" if you're calculating futures.
- afavour 2y agoYes but JS always has backwards compatibility in mind, even if it wasn’t in the spec. Wouldn’t be surprised if more modern implementations still add an arbitrary restriction.
- asveikau 2y agoThere's a shocking amount of systems that still have 32 bit time_t. Linux and glibc only started supporting it on 32bit systems in the current decade.
- darepublic 2y agoinstead 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).
- deleted 2y ago[deleted]
- augusto-moura 2y agoAre you suggesting checking the date every frame vs scheduling long task every once in a long while? Can't tell if it is ironic or not, I'm sorry (damn Poe's law). But assuming not, it would be a lot more computationaly expensive to do that, timeouts are very optmized and they "give back" on the computer resources while in the meantime
- darepublic 2y agoNo irony intended I can be this dumb. Your point did occur to me as I posted, was just grasping at straws for a "clean" solution
- jw1224 2y agoUnlike setTimeout, requestAnimationFrame callbacks are automatically skipped if the browser viewport is minimized or no longer visible. You wouldn’t want to miss the frame that matters!
- chii 2y agoalso, not to mention that setBigTimeout would still work in serverside js, while requestanimationframe doesn't!
- hiccuphippo 2y agoSo the js engine converting the javascript number (a double?) To an int and it's rolling over?
- jackconsidine 2y agoThis 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)
- Waterluvian 2y agoTrue. Though if you have a need to trigger something after that much time, you might recognize the need to track that scheduled event more carefully and want a scheduler. Then you’ve just got a loop checking the clock and your scheduled tasks.
- jackconsidine 2y agoRight on. Pretty quickly that's the better solution
- keyle 2y agoThis is great for the folks running serverless compute! You get to start a process and let it hang until your credit card is maxed out. /s
- hmaxdml 2y agoThat was before DBOS -- the serverless platform that bills you only for CPU time, not wall clock time ;) see https://www.dbos.dev/blog/aws-lambda-hidden-wait-costs https://www.dbos.dev/blog/aws-lambda-hidden-wait-costs
- internetter 2y agoI don't see how this pricing (or product in general) is any better than cloudflare workers. To be clear, I am not trying to be mean, I'm just curious to hear why I would pick this over cf.
- leni536 2y agoSo... do they not charge for sitting idle and consuming memory?
- ingen0s 2y agoYou have captured my heart and imagination
- throwaway14356 2y agobecause no one asked. If you need shorter intervals than the minimum you can make a function that calls the other function multiple times in a row.
- keepamovin 2y agoThis is excellent. But I was hoping for a setTimeout that survived JavaScript environment restarts. Maybe setBigReliableTimeout is in your future? Hahaha! :)
- ipython 2y agoSounds a lot like the famous windows 95 bug when it would crash after 49.7 days of uptime [1] [1] https://news.ycombinator.com/item?id=28340101 https://news.ycombinator.com/item?id=28340101
- sehugg 2y agoGetTickCount() still exists and still returns a DWORD.
- n_plus_1_acc 2y agoIn 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?
- zeven7 2y agoAdding a comment here to check back later because I'm curious now if someone has the answer. I thought it would be easy to find the answer, but I can't find it either. I figured it would say somewhere a number is converted to an int32, but instead I got to the part where there's a map of active timers[1] with the time stored as a double[2] without seeing a clear loss happening anywhere before that. [1] https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#map-of-active-timers https://html.spec.whatwg.org/multipage/timers-and-user-promp... [2] https://w3c.github.io/hr-time/#dom-domhighrestimestamp https://w3c.github.io/hr-time/#dom-domhighrestimestamp
- vilius 2y agoHere’s a deep dive in 6 minutes https://youtu.be/boD0ReK62FI?si=jSXuQn0DHn3riJgd https://youtu.be/boD0ReK62FI?si=jSXuQn0DHn3riJgd Just JS being JS: setTimeout(()=>{}, Infinity) executes immediately
- n_plus_1_acc 2y agoThanks, but I'm looking for the specification of this behaviour.
- bufferoverflow 2y agosetTimeout is stranger than you think. 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.
- jonathanlydall 2y agoInteresting. Maybe the system clock did a network time synchronisation during the setTimeout window.
- gregoriol 2y agoI don't think there is any guarantee that setTimeout will run at exactly 1000. Though didn't expect it to run earlier, it definitely could run later.
- bufferoverflow 2y agoSame. I expected it could take a few ms longer. But less? Apparently that's a thing.
- _flux 2y agoI wonder if your 999ms was measured using wall-clock time or a monotonic time source? I imagine a wee time correction at an inopportune time could make this happen.
- steve_adams_86 2y agoWhy does your unit test need to wait one second? Or are you controlling the system time, but it still had that error?
- bufferoverflow 2y agoHow else would you test if something happens after 1 second or not?
- 2y ago
- steve_adams_86 2y agoThis makes me love having Go handy. I find working with signals and time based events so much nicer than other languages I use. This is fun, though. JS is a bucket of weird little details like this.
- oefrha 2y agoGo timers do have weird little details, in fact one little detail changed recently in 1.23 and broke my code. A third party dependency selects on sending to a channel and time.After(0); before 1.23, due to timer scheduling delay, the first case would always win if the channel has capacity to receive, but since 1.23 the timer scheduling delay is gone and the “timeout” wins half the time. The change is documented at https://go.dev/wiki/Go123Timer https://go.dev/wiki/Go123Timer but unless you read release notes very carefully (in fact I don’t think the race issue is mentioned in 1.23 release notes proper, only on the separate deep dive which is not linked from release notes) and are intimately familiar with everything that goes into your codebase, you can be unexpectedly bitten by change like this like me.
- steve_adams_86 2y agoOh, interesting. That’s legitimately annoying. I have a huge scheduling component in an application that could be (probably is) impacted by this, so thanks for the heads up!
- h1fra 2y agoKeeping a server alive for more than 25days is a feat in this serverless world
- alamortsubite 2y agoThe longer the delay, the more likely the process is to crash before the timer completes. Use a scheduler instead.
- bhauer 2y agoCorrect take. But I also want to point out that this earnest reply is casting "remove curse" on this cursed library.
- purplesyringa 2y agoInterestingly, this library seems to suffer from the opposite problem: where setTimeout can trigger earlier than expected, setBigTimeout can trigger never at all! 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: > 1e16 - 1 == 1e16 true 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. 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!
- paulddraper 2y agoI would go so far as to say entirely theoretical.
- rzz3 2y agoAwesome! Already have a project I can use this on, thanks. 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.
- malthejorgensen 2y agosourcehut isn’t weird at all. It’s made by Drew Devault who is mostly well-respected in the hacker community, and it’s made exactly to be an alternative to BigCo-owned source hosts like GitHub, Gitlab and Bitbucket.
- cchcch 2y agoDrew isn't well-respected, he's been far too antagonistic to far too many people over the years for that. Latest news is that he authored/published a controversial character assassination on Richard Stallman while trying and failing to stay anonymous. Then some further digging after this unmasking found he's into pedophilic anime. Sitting on his computer uploading drawings of scantily-clad children to NSFW subreddits. No-one with any decency can respect that behavior, it's disgusting.