1

I understand that setTimeout doesn't necessarily fire at the exact delay you specify, because there could be other items in queue at the instant that the timeout occurs and the engine will do those things first (further delaying the time you've specified).

However, I'm wondering if it does take sub-millisecond inputs into consideration at all. For example, if I input 1.12345678ms, behind the scenes does it attempt to fire at that exact time, or does it parseInt the sub-millisecond value I've inputed before even truly setting the actual timeout (under the hood)?

Furthermore, let's say I'm determining the ms delay with long division and that division produces an exponent like 1.2237832530049438e-9. Do I need to parseInt that exponent before handing it to setTimeout(()=>{},ms) or will setTimeout do the right thing (as long as it is some type of number) without me ever having to worry about prepping the input?

Update: Here's a snippet of setTimeout dealing with smaller and smaller sub-millisecond delay values:

let count = 0;
function consoleLog(timeOut)
{
    let now = Date.now();
    timeOut = (timeOut / 1.12345);
    setTimeout(()=>
    {
        count += 1;
        if (count <= 6444)
        {
            console.log(`Timestamp ${now}`,`Timeout: ${timeOut}`,`Count: ${count}`);
            consoleLog(timeOut);
        }
    });
}
consoleLog(1000);

Warning, the code above recurses 6,444 times in order to show that there comes a point where the timeOut value no longer gets smaller from dividing it further: after count 6440, the timeout sustains 2e-323 thereafter.

Lonnie Best
  • 9,936
  • 10
  • 57
  • 97
  • 2
    Most browsers store the delay internally as an integer. Also, most browsers will impose a 4ms minimum interval. – Pointy Aug 11 '20 at 15:10

2 Answers2

1

Modern browsers throttle setTimeout/setInterval calls to a minimum of once every 4 ms.

Also, MDN says that:

The delay argument is converted to a signed 32-bit integer. This effectively limits delay to 2147483647 ms, since it's specified as a signed integer in the IDL.

So, any fractions of milliseconds are not going to be effective.

The times are not JS specifications - they are specified in the DOM standards.

4 ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), the minimum timeout value for nested timeouts was 10 ms.

However in Node JS, the timers used are the system-specific high precision timers. They (the system timers) can go in resolutions up to nanoseconds. It should be experimented in Node JS if the timer delays are saved as integers.

Charlie
  • 22,886
  • 11
  • 59
  • 90
  • 3
    According to current node docs, a delay value that's less than 1 will be treated as 1. – Pointy Aug 11 '20 at 15:35
  • Thanks for addressing the effectiveness. Is it safe to input any number (like sub-millisecond values or exponents that happen during division)? Or, should I `parseInt(value,10)` before passing a delay argument into setTimeout? – Lonnie Best Aug 11 '20 at 15:44
  • 2
    It is safe to pass any value. Browser will convert that to integer. – Charlie Aug 11 '20 at 15:54
  • @Pointy Thanks for the info – Charlie Aug 11 '20 at 15:59
1
ExceptionOr<int> DOMWindow::setTimeout(JSC::JSGlobalObject& state, std::unique_ptr<ScheduledAction> action, int timeout, Vector<JSC::Strong<JSC::Unknown>>&& arguments)
{
    auto* context = scriptExecutionContext();
    if (!context)
        return Exception { InvalidAccessError };

    // FIXME: Should this check really happen here? Or should it happen when code is about to eval?
    if (action->type() == ScheduledAction::Type::Code) {
        if (!context->contentSecurityPolicy()->allowEval(&state))
            return 0;
    }

    action->addArguments(WTFMove(arguments));

    return DOMTimer::install(*context, WTFMove(action), Seconds::fromMilliseconds(timeout), true);
}

According to source code for setTimeout it takes int as input. Which is a 32-bit signed integer.

So, the answer is, no. It does not takes into consideration.

Ali Demirci
  • 5,302
  • 7
  • 39
  • 66
  • Do I need to `parseInt`, or can I depend on the engine to make conversions that are as close as possible to the sub-millisecond values and exponents I'll be passing to `setTimout`? You see, I'm dividing numbers to determine the delay and they're not always integers after that division. – Lonnie Best Aug 11 '20 at 15:48
  • 1
    According to section 9.4 of 5th ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-9.4) `ToInteger` function returns a result of `sign(number) × floor(abs(number))`. So that means you don't have to `parseInt` as it already does that for you. But see the algorithm, it floors the value, which means if you put `1.6` it will floored down to `1`. – Ali Demirci Aug 12 '20 at 07:41
  • 1
    Great answer, btw. You back up everything you say with authoritative sources. – Lonnie Best Aug 12 '20 at 21:18