javascriptevent-loop

Different order for timers when spliting in 2 calls or in a single


In the below code I run 2 sets of tests where a timer is either split in 2 nested calls to setTimeout() and a single call with the timeout set to the sum of both previous calls.

However, I notice that the ordering isn't the same when the summed up timer is 30ms and when it's 50ms:

setTimeout(() => {
    setTimeout(() => {
        console.log("Timeout 1 (40+10ms)");
    }, 10);
}, 40);

setTimeout(() => {
    console.log("Timeout 2 (50ms)");
}, 50);


setTimeout(() => {
    setTimeout(() => {
        console.log("Timeout 1 (20+10ms)");
    }, 10);
}, 20);

setTimeout(() => {
    console.log("Timeout 2 (30ms)");
}, 30);

In my Chrome browser I get for the 50ms timer

  • Timeout 1 (40+10ms)
  • Timeout 2 (50ms)

While for the 30ms one, I get

  • Timeout 2 (30ms)
  • Timeout 1 (20+10ms)

Aren't these supposed to be the same?
Why does only changing the time in delay produces a different order?


Solution

  • Per specs, the ordering is not defined in this case. They do ask to wait for previous invocations that had a smaller timeout to be executed first, but here we're in the undefined case of a smaller timer that's been set later. So both orders are allowed.

    Now, if Chrome switches the ordering specifically at 32ms, it's because of this CL. According to the comments in the code there, that would be to accommodate with some tests they were running for removing 1ms clamping in setTimeout(, 0). The logic exposed in the comments read:

    // A timer with a long timeout probably doesn't need to run at a precise time,
    // so allow some leeway on it. On the other hand, a timer with a short timeout
    // may need to run on time to deliver the best user experience.

    Though it seems they were supposed to remove this logic once the experiments would be over (which I believe they are).