whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
7.86k stars 2.58k forks source link

setTimeout() nesting levels: browsers do not match specification #10386

Open majaha opened 1 month ago

majaha commented 1 month ago

What is the issue with the HTML Standard?

None of Chrome, Firefox or Safari match the timer initialization steps. In particular, the amount of nesting that is required before the timeout is clamped to 4ms differs.

The issue is demonstrated with this code:

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <button id="run">Run</button>
  <script>
    let last = 0;
    let id = 0;
    let count = 10;

    function timeout() {
      let now = performance.now();
      console.log(now-last);
      last = now;
      id = setTimeout(timeout, 0);
      if (!count--) {
        clearInterval(id);
      }
    }

    function run() {
      console.log("START");
      count = 10;
      last = performance.now();
      clearInterval(id);
      id = setTimeout(timeout, 0);
    }

    document.querySelector("#run").addEventListener("click", run);

  </script>
</body>

</html>

By a close reading of the spec, you can trace the execution like this:

time: 0
run() 
  log("START")
  setTimeout(timeout) (nesting level: 0, timeout: 0)
time: 0
timeout()
  log(0)
  setTimeout(timeout) (nesting level: 1 timeout: 0)
time: 0
timeout()
  log(0)
  setTimeout(timeout) (nesting level: 2 timeout: 0)
time: 0
timeout()
  log(0)
  setTimeout(timeout) (nesting level: 3 timeout: 0)
time: 0
timeout()
  log(0)
  setTimeout(timeout) (nesting level: 4 timeout: 0)
time: 0
timeout()
  log(0)
  setTimeout(timeout) (nesting level: 5 timeout: 0)
time: 0
timeout()
  log(0)
  setTimeout(timeout) (nesting level: 6 timeout: 4)
time: 4
timeout()
  log(4)
  setTimeout(timeout) (nesting level: 7 timeout: 4)
...
...

Which means that by the spec, the output should be approximately:

START
0
0
0
0
0
0
4
...
...

with six 0's before the first 4.

However, both Firefox and Chrome print only four 0's before the first 4. And I think Safari prints five 0's.

So step 5 of the spec doesn't match what implementations are actually doing.

annevk commented 3 weeks ago

cc @domenic

majaha commented 4 days ago

I found this in the Chrome code, it looks like I'm not the only one who's spotted this: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/scheduler/dom_timer.cc;l=272;drc=b0b102b6582fe1fca4a5eb6b156f198113674ec7;bpv=1;bpt=1?q=dom_timer.cc&ss=chromium%2Fchromium%2Fsrc

https://issues.chromium.org/issues/40141482