2

const prev = performance.now();
requestAnimationFrame(now => console.log(now - prev));

Why now is always less than prev (at least in the last Chrome branches)?

Screenshot:

Screenshot

Kaiido
  • 123,334
  • 13
  • 219
  • 285
vsemozhebuty
  • 12,992
  • 1
  • 26
  • 26
  • It shows me a positive number. – tevemadar Dec 04 '20 at 23:17
  • I get a negative in the Chrome Canary And Dev. – vsemozhebuty Dec 04 '20 at 23:19
  • 1
    Ok, that was Safari on iOS. On my PC it's really negative for Chrome. But it's positive for Edge (the Chromium-based one) and also in Firefox. – tevemadar Dec 04 '20 at 23:24
  • Then maybe this is a Chrome bug. Did you test on stable Chrome? – vsemozhebuty Dec 04 '20 at 23:26
  • 1
    It varies from positive to negative on different runs for me (Chromium) – Daniel_Knights Dec 04 '20 at 23:32
  • 1
    As to why, just seems like a timing inaccuracy. From [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now): "It's important to keep in mind that to mitigate potential security threats such as Spectre, browsers typically round the returned value by some amount in order to be less predictable. This inherently introduces a degree of inaccuracy by limiting the resolution or precision of the timer." – Daniel_Knights Dec 04 '20 at 23:33
  • 1
    Stable, though older (86.0.4240.111, wants to update). The effect may have some explanation in *When multiple callbacks queued by requestAnimationFrame() begin to fire in a single frame, each receives the same timestamp even though time has passed during the computation of every previous callback's workload* (MDN, requestAnimationFrame), so the timestamp have some space for interpretation... – tevemadar Dec 04 '20 at 23:34
  • 1
    ... and *It's important to keep in mind that to mitigate potential security threats such as Spectre, browsers typically round the returned value by some amount in order to be less predictable. This inherently introduces a degree of inaccuracy by limiting the resolution or precision of the timer.* (MDN, performance.now) - so it has some deliberate inaccurracy. (oh, I see this one was posted already) – tevemadar Dec 04 '20 at 23:35
  • But I would expect that both values were rounded then and still the first one would be less... – vsemozhebuty Dec 04 '20 at 23:39
  • 1
    DOMHighResTimestamp jitter is on the order of less than a ms on every browsers, this has nothing to do with the issue here, which is more about browsers using the "VSync pulse" time (i.e not linked to the event loop), and a bug in Chrome which makes it fire rAF callbacks ASAP when there is no other animation frame scheduled. – Kaiido Dec 05 '20 at 03:23

1 Answers1

0

This happens because you request an animation between current frame and the next repaint but you could actually have missed the first callback trigger of the current frame which is when the timestamp argument was established.

Many calls to requestAnimationFrame callback could occur in this timespan, but all of them will have the timestamp of the first requestsAnimationFrame's callback trigger in the active frame which may have occurred before your call to performance.now().

MDN also states:

The callback function is passed one single argument, a DOMHighResTimeStamp similar to the one returned by performance.now(), indicating the point in time when requestAnimationFrame() starts to execute callback functions.

Michalis Garganourakis
  • 2,872
  • 1
  • 11
  • 24
  • But I run just these two lines in the console for sites like http://example.com/ which have no other JS at all... – vsemozhebuty Dec 04 '20 at 23:44
  • Still your request for animation frame could be in the middle of this time or right before the next repaint, so the `timestamp` (your `now` variable) could be outdated, because it's actually the timestamp of the first callback triggered by `requestAnimationFrame` of current frame that you don't even had the chance to catch. – Michalis Garganourakis Dec 04 '20 at 23:52
  • 1
    But as queueing the callback didn't happen in a `requestAnimatonFrame` callback, the earliest animation frame has to be later than queueing. There must be something more going on there... – FZs Dec 04 '20 at 23:56
  • 1
    Repaints happen *after* a JS task finishes, but not during a task! – FZs Dec 04 '20 at 23:57
  • 1
    If I understand correctly, DOMHighResTimeStamp for a bunch of callbacks would be the time of the start of the first callback and it cannot be less than the time of the scheduling of the first callback or the `performance.now();` call before the scheduling. – vsemozhebuty Dec 05 '20 at 00:12