Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Technical review: Long Animation Frame API docs #32937

Closed

Conversation

chrisdavidmills
Copy link
Contributor

@chrisdavidmills chrisdavidmills commented Apr 3, 2024

Note: This technical review is now completed and approved. For the follow-on editorial review, see #33039


Description

The Long Animation Frames API was released in Chrome 123. This PR adds docs for this API.

Specifically, it:

  • Adds a guide to using the API
  • Adds reference docs for the PerformanceLongAnimationFrameTiming interface.
  • Adds reference docs for the PerformanceScriptTiming interface.
  • Adds entries to existing docs that require them, such as https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry/entryType
  • Updates GroupData.json so that the relevant new items appear in the Performance API sidebar

Motivation

Additional details

Related issues and pull requests

@chrisdavidmills chrisdavidmills requested review from a team as code owners April 3, 2024 11:46
@chrisdavidmills chrisdavidmills requested review from wbamberg and pepelsbey and removed request for a team April 3, 2024 11:46
@github-actions github-actions bot added the Content:WebAPI Web API docs label Apr 3, 2024
@chrisdavidmills chrisdavidmills marked this pull request as draft April 3, 2024 11:46
@github-actions github-actions bot added the size/m [PR only] 51-500 LoC changed label Apr 3, 2024
Copy link
Contributor

github-actions bot commented Apr 3, 2024

Preview URLs (23 pages)
Flaws (5)

Note! 18 documents with no flaws that don't need to be listed. 🎉

URL: /en-US/docs/Web/API/PerformanceScriptTiming/sourceCharPosition
Title: PerformanceScriptTiming: sourceCharPosition property
Flaw count: 1

  • bad_bcd_queries:
    • No BCD data for query: api.PerformanceScriptTiming.sourceCharPosition

URL: /en-US/docs/Web/API/PerformanceScriptTiming/invokerType
Title: PerformanceScriptTiming: invokerType property
Flaw count: 1

  • macros:
    • /en-US/docs/Web/API/Window/setTimeout redirects to /en-US/docs/Web/API/setTimeout

URL: /en-US/docs/Web/API/PerformanceScriptTiming/sourceFunctionName
Title: PerformanceScriptTiming: sourceFunctionName property
Flaw count: 1

  • bad_bcd_queries:
    • No BCD data for query: api.PerformanceScriptTiming.sourceFunctionName

URL: /en-US/docs/Web/API/PerformanceScriptTiming/sourceURL
Title: PerformanceScriptTiming: sourceURL property
Flaw count: 1

  • bad_bcd_queries:
    • No BCD data for query: api.PerformanceScriptTiming.sourceURL

URL: /en-US/docs/Web/API/Performance_API/Long_animation_frame_timing
Title: Long animation frame timing
Flaw count: 1

  • macros:
    • /en-US/docs/Web/API/PerformanceObserver/supportedEntryTypes redirects to /en-US/docs/Web/API/PerformanceObserver/supportedEntryTypes_static
External URLs (10)

URL: /en-US/docs/Web/API/PerformanceScriptTiming
Title: PerformanceScriptTiming


URL: /en-US/docs/Web/API/PerformanceScriptTiming/forcedStyleAndLayoutDuration
Title: PerformanceScriptTiming: forcedStyleAndLayoutDuration property


URL: /en-US/docs/Web/API/Performance_API/Long_animation_frame_timing
Title: Long animation frame timing


URL: /en-US/docs/Web/API/PerformanceLongAnimationFrameTiming/blockingDuration
Title: PerformanceLongAnimationFrameTiming: blockingDuration property

(comment last updated: 2024-04-11 11:54:41)

@github-actions github-actions bot added size/l [PR only] 501-1000 LoC changed and removed size/m [PR only] 51-500 LoC changed labels Apr 3, 2024
@wbamberg wbamberg requested review from Elchi3 and removed request for wbamberg April 3, 2024 16:38
@chrisdavidmills chrisdavidmills marked this pull request as ready for review April 4, 2024 13:24
@github-actions github-actions bot added size/xl [PR only] >1000 LoC changed and removed size/l [PR only] 501-1000 LoC changed labels Apr 4, 2024
@chrisdavidmills chrisdavidmills changed the title Long Animation Frame API docs Technical review: Long Animation Frame API docs Apr 4, 2024
Copy link
Contributor

@tunetheweb tunetheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good, but then I can't complain too much since it's based a lot on our docs :-)

Did spot a few things.

@noamr should also review when he gets a chance.


{{SeeCompatTable}}{{APIRef("Performance API")}}

The **`blockingDuration`** readonly property of the {{domxref("PerformanceLongAnimationFrameTiming")}} interface returns a {{domxref("DOMHighResTimeStamp")}} indicating the total time the animation frame was being blocked, in milliseconds.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should expand a bit?
The idea here is that some long animation frames could be caused by the browser batching frames together and throttles rendering, to avoid flashes and unnecessary rendering. blockingDuration is the "net" duration this frame would be blocking for, if such throttling hadn't taken place. Happy to expand more

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think I need more explanation, to work out what description to add. From your previous comment, it sounds like blockingDuration is a measure of some blocking that isn't actually happening because the browser has batched frames together and throttled rendering. In which case, since the browser has handled it, why is it useful?

Or is it the case that the reported blocking (and LoAF) is occurring because the developer's code is written in a way that means the browser can't do the batching and rendering, and the code needs to be updated in a way that allows for this?

Or was I correct the first time round, and the blockingDuration is actually a measure of how much worse it would be if the browser did not batch and throttle? Which is still confusing me.

From looking at @tunetheweb's example that I included in the main long animation frames guide, it looks like blockingDuration is simply a measure of how long the LoAF blocked the main thread from doing anything else...but from what you are saying, it is more complicated than that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works best with an example.
Let's say you fetched a resource, and now you have 100ms worth of JS to process.
If you yield in the middle (e.g. split with a setTimeout), the browser decide to (a) render a frame and only then run the rest of the script, or (b) wait until all of the JS is run and then render the frame. You can also decide yourself to not yield at all (c).

If you didn't yield at all, your duration would be 100ms and the blocking duration will be 50ms. (a)
If you yielded, and the browser decided to wait until all the JS is run, the duration will be 100ms and the blocking duration will be 0 - that's because you let the browser handle higher priority work. (b)
If you yielded and the browser decided to render, the duration will be 50ms and the blocking duration will be 0. (c)

So if you're measuring pure UX, you should use duration. You could diminish the duration by having less JS overall in this case.
If you're measuring effect on responsiveness, you probably want to go with blockingDuration - because the browser (at least chromium) will let input events execute if you yield.

Does this make more sense?

Copy link
Contributor

@tunetheweb tunetheweb Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think you switched your a, b, and c's between those two paragraphs :-)

I'm not sure I understand the yielding example, where the browser decided to execute anyway. Doesn't that still create a blocking period during that 100ms execution? I agree you gave it a chance to yield, but then when it didn't take that chance to yield it blocked for 100ms (50ms blocking time).

Is a better example this?:

Let's say you have some JavaScript which executed for 65ms, and then have another 80ms of processing to complete. If you yield after that first 65ms (e.g. split with a setTimeout), the browser can decide to (a) render a frame and only then run the rest of the script, or (b) wait until all of the JS is run and then render the frame. You can also decide yourself to not yield at all (c).

That would leave to the following for each scenario:

  duration (LoAF1) blockingDuration (LoAF1) duration (LoAF2) blockingDuration (LoAF2)
a 65 15 (65 - 50) 80 30 (80-50)
b 145 (65 + 80) 45 ((65 - 50) + (80 - 50)) 0 0
c 145 (65 + 80) 95 ((65 + 80) - 50) 0 0

Note the total duration in all three instances is 145ms, and the total blocking duration in the first two is the same (45ms), but 50ms longer in the second as the task was not split into two.

Assuming I've got that right, is this better phrased as something like:

Suggested change
The **`blockingDuration`** readonly property of the {{domxref("PerformanceLongAnimationFrameTiming")}} interface returns a {{domxref("DOMHighResTimeStamp")}} indicating the total time the animation frame was being blocked, in milliseconds.
The **`blockingDuration`** readonly property of the {{domxref("PerformanceLongAnimationFrameTiming")}} interface returns a {{domxref("DOMHighResTimeStamp")}} indicating the total time the animation frame was being blocked, in milliseconds. Blocked is defined as the sum of times above 50ms for each task in the frame, with the frame rendering time included in the longest task time rather than it's own task.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edited above comment to correct some figures.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Not exactly. blockingDuration is the time the main thread was blocked from responding to high priority tasks, such as input.
  2. Yes, with the rendering duration duration counted as part of the longest task
  3. Great question! Happy to (try to) explain

duration measures the response time of this LoAF in practice, while blockingDuration measures potential effect on responsiveness - which is more equivalent to jank. It tries to answer the question "given input during this frame, for how long will this frame feel janky?"

In terms of timeline visualization, think of startTime -> (startTime + duration) to be a big yellow bar that overlaps many things like scripts, ends with rendering, and has lots of "red" spots inside that are the blocking ends of the long tasks. blockingDuration sums the red spots.

For the purposes of jank, blockingDuration is really what matters. duration is important for correlation - you use it to understand, for example, whether style+layout took a long time, and what other things

Copy link
Contributor

@tunetheweb tunetheweb Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way is to think about it like this. Say you had these tasks to process:

  1. 5ms
  2. 10ms
  3. 120ms

Well for a start the browser doesn't know the length in advance. So we have some special knowledge here cause we can see in to the future, but go with me here.

At any point a more important task could come along. These are generally user-input (e.g. the user clicked on a button and we want to action that first, rather than any other queued tasks which are likely of lesser importance) and rendering (the browser tries to render every 16ms).

So it starts processing task 1. Then it can take a break. Before moving on to task 2 it can check if there's any other user inputs (nope!) or if it's time to render (also nope!).

So it starts processing task 2. We're now up to 15ms. If there's still no user input, and it's still not rendering time (though at 15ms it's pretty close so maybe the browser will decide to render here - but let's assume not).

So it starts processing task 3. This holds up the browser from doing anything for a really long 120ms. That's not great. Ideally we like it to process for 50ms max—well ideally a lot shorter than that even! Especially if you want to render every 16ms! But we're realistic here and so we set a "this is awful" target of 50ms.

Think of it like < 16ms is "good", 16ms - 50ms is "not great - needs improvement", and > 50ms is "poor".

Maybe once we've "solved" 50ms being quick normally, we can maybe reduce long tasks to be anything > 16ms? But think we've a long way to go before we can even consider that! But that would be ideal. Though of course by then everyone will be using monitors that are > 60Hz (16ms a frame), so we'll have even less than that. The work never stops!!

Anyway, we end up with a LoAF of 135ms (plus whatever rendering time), of which 70ms was "blocking time" from that third task.

Now let's do the same thing, but this time with a user input that takes 30ms and that happens somewhere while the browser is processing step 2.

In that case it processes the first task for 5ms, then the second for 10ms, then it sees the input so bumps task 3 to task 4. After processing that user input it's now done 45ms of processing in total so it decides to take a break there and fit in a render. The long 120ms task gets bumped to the next frame.

And in that case, assuming the rendering is quick, this first frame might still be under 50ms and not report a LoAF at all! Which is great. Admittedly it's not 16ms but, as per above, we've gotta be realistic as to what we can do here.

With smaller tasks the browser has more options to bump things. With longer tasks (above 50s) the browser can't do that as much and it's probably gonna be noticeable—it's definitely going to be a long task AND a long frame.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the help on this, @tunetheweb and @noamr. I've written it all up into an extended "Description" section on the blocking duration ref page, and linked to it from relevant places.

See 207a694 for exactly what changed in that commit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah see you used the exact example. Maybe a little detailed, but does explain it well so I like it.

Looks technically correct but made one comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup — it is a really good example, and helped me understand exactly what is going on ;-)

@tunetheweb
Copy link
Contributor

PR LGTM from a technical point of view.

@chrisdavidmills
Copy link
Contributor Author

Awesome, thanks @tunetheweb. I will now consider the tech review completed, close this PR, and open a new PR based on the same branch to contain the editorial review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Content:WebAPI Web API docs size/xl [PR only] >1000 LoC changed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants