-
Notifications
You must be signed in to change notification settings - Fork 707
[css-view-transitions-1] Should the DOMChangeCallback be posted as a task from skip transition steps? #7904
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
Comments
The downside of making it async is the following sequence:
It would be nice if the callback for the previous one consistently happened before or after we capture for the new one. But that depends on whether the event loop already has a task to run the next frame? I think the answer to this is a microtask which would ensure that this runs before the next frame. @flackr on that. Microtask won't help if multiple transitions are set up within the same frame but that seems like an edge case that the developer can handle better. |
Summary from offline discussion. Microtasks need a checkpoint to ensure they run before a specific event. Since there is already a checkpoint for Web Animations, using a microtask will ensure the callback runs before the next rAF. It is brittle to rely on an unrelated checkpoint for this timing though. Also since the callback itself is async, it's already possible that it hasn't finished before the next rAF. The domUpdated promise was added precisely so developers have a hook to know when the callback is done. That could be used to delay queueing another transition if it should deterministically start after the callback for the first one is done. Proposed Resolution: Invoking the dom-update-callback is done by positing a task in skip the page transition. |
If I'm following correctly the proposal is to always post as microtask? |
The proposal is to use a task instead of microtask. Microtask timing can be unpredictable since the microtask checkpoint which runs them can be triggered by any feature. |
@ydaniv (and/or anyone else) are you OK with the clarification and proposed resolution? |
@astearns SGTM! |
OK, seeing consensus here so this should get in the spec for wider review. The CSSWG will automatically accept this resolution one week from now if no objections are raised here. Anyone can add an emoji to this comment to express support. If you do not support this resolution, please add a new comment. Proposed Resolution: Invoking the dom-update-callback is done by positing a task in skip the page transition. |
RESOLVED: Invoking the dom-update-callback is done by positing a task in skip the page transition. |
This would mess up the phases. By making Note that the change callback is called synchronously during the update phase also when it's not skiped, when preparing the transition. IMO the callback should be called synchronouosly or the whole state machine becomes botched. |
That's not feasible because we can execute this step very late in the update the rendering steps. Specifically, step 8.7.5 :
The "Handle transition frame" algorithm is triggered here, right before step 16. The goal behind running it this late is that its supposed to reflect DOM changes into the corresponding pseudo-elements. So we want to run it at a point when script is guaranteed to not modify the DOM. But that also means we're at a point where running script is bad. It won't be logically incorrect for this feature but adding a hook where script can run implies the DOM can be dirtied and we'll need to re-run style/layout again. We can make the async approach by doing the following:
I'll try to dig up the exact issue but IIRC we resolved on the fact that |
I don't get why running scripts here is bad? It's not different from running scripts in resize/intersection observers/rAF. What adding a postTask here would cause is a potential flicker. you'd have a moment where on one hand rendering is allowed ("transition suppressing rendering" is off), and OTOH the update callback wasn't called. In the current spec, from the moment you start a transition it is guaranteed that there's no intermediate rendering until the transition is resolved. That's great. With this change, there would be this uncanny-valley moment of "the previous transition has not resolved but rendering is allowed". Of course, we could also put step 5 (Set transition suppressing rendering to false) in that task, but what would we gain? Allowing event/timeout handling while rendering is suppressed, which has to now take the intermediate suppressed state into account? If we suppress rendering anyway, we might as well do everything synchronously and perform an additional style and layout if needed. |
Just reiterating the case which could potentially flicker: function cancelTransition(transition) {
// Author assumes that update callback was dispatched synchronously here.
transition.skipTransition();
// Author updates DOM assuming the update callback was already run.
// This causes a flicker when the update callback is dispatched.
updateDOMAfterTransition();
} The update callback itself is async. In the case above, even if dispatched the update callback synchronously in skipTransition, the author still has to ensure its completed before running function cancelTransition(transition) {
transition.skipTransition();
transition.updateCallbackDone.then(updateDOMAfterTransition);
} Also note that we don't want to suppress rendering until the callback finishes if the transition was skipped. We don't know if the author will make the updateDOM callback a no-op in this case. |
Thanks @khushalsagar, this gives me enough context, I'll proceed with a PR based on the resolution. |
Amend assert to allow calling the callback from a "done" state The "finished" promise is bound to the update callback promise, so it doesn't need to change (the update callback is async anyway). See [resolution](w3c#7904 (comment)). Closes w3c#7904
…te callback from a task (#8827) * When skipping transitions, call the DOM-update callback from a task Amend assert to allow calling the callback from a "done" state The "finished" promise is bound to the update callback promise, so it doesn't need to change (the update callback is async anyway). See [resolution](#7904 (comment)). Closes #7904 * Add changelist entry
Step 4 in skip the page transition algorithm says that the DOM change callback has to be invoked synchronously. However, the skip the page transition algorithm sometimes runs as part of the update the rendering, and we really want to avoid running (new) script in those steps.
We should make it asynchronous. The question is should we just make it asynchronous for all places that run the skip the page transition algorithm, or should it be conditional?
I'm leaning towards just make it all asynchronous, but I'm unsure if there are cases where synchronous invocation is important.
/cc @khushalsagar @jakearchibald
The text was updated successfully, but these errors were encountered: