-
Notifications
You must be signed in to change notification settings - Fork 22.5k
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
Technical review: Long Animation Frame API docs #32937
Conversation
There was a problem hiding this 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.
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/performancelonganimationframetiming/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Show resolved
Hide resolved
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/api/performance_api/long_animation_frame_timing/index.md
Outdated
Show resolved
Hide resolved
|
||
{{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. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Not exactly.
blockingDuration
is the time the main thread was blocked from responding to high priority tasks, such as input. - Yes, with the rendering duration duration counted as part of the longest task
- 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
There was a problem hiding this comment.
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:
- 5ms
- 10ms
- 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ;-)
files/en-us/web/api/performancelonganimationframetiming/blockingduration/index.md
Outdated
Show resolved
Hide resolved
PR LGTM from a technical point of view. |
…ngduration/index.md Co-authored-by: Barry Pollard <barrypollard@google.com>
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. |
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:
PerformanceLongAnimationFrameTiming
interface.PerformanceScriptTiming
interface.Motivation
Additional details
Related issues and pull requests