[WICG/webcomponents] Idea: A tree-aware task scheduler (Issue #1055)

## Idea 

I think it may be very useful to have a way to schedule DOM update tasks in a way that the DOM will take care of executing them in tree-order.

Something like:

```ts
interface Element {
  queueDOMUpdate(callback: () => void): Promise<void>;
}
```

## Why

Batched and ordered DOM updates are important for performance and often correctness. Currently this batching and ordering most often is either centralized by frameworks, or distributed but _emergently coordinated_ in common web components libraries.

The emergent order of custom elements generally happens this way:

* Initial render
  * The browser schedules CE reactions for new elements from parsed HTML in tree order
  * Instances enqueue an render microtask in their constructor or connectedCallback
  * So the microtask queue is tree ordered
  * The render tasks create more child elements, whose CE reactions are queued, which then enqueue more microtasks...
* Updates
  * Some state for an element is changed (attributeChangedCallback, a JS setter or method call)
  * The element schedules an update microtask
  * The microtask may update children, which enqueue their own update microtasks...

This pattern results in top-down rendering when we have top-down data flow, with the only coordination point being use of the global microtask queue resource.

It's robust to common DOM update patterns too, especially those using DOM events. A element may handle some user input and dispatch an event. The listener of the event may control some state that it changes in response. The new state flows down the tree, triggering updates in tree-order.

Tree-ordered rendering doesn't always emerge in situations where you have cross-tree shared observable state though. You may have a central data store (Redux, MobX, etc.) and if components aren't notified of changes in the order that they subscribe, or they manage to subscribe in out-of-tree-order (either because of upgrades, conditional subscriptions, or tree modifications) then children may be notified before parents.

This can cause a child to update twice if it depends both on the global store and passed-down state that the parent derives from the global store. It also can cause children to update that are removed by the parent due to the state change.

Currently, component/global-state integrations would have to take care to preserve tree-ordered notifications, and that ordering would only be maintained for that integration library - it likely wouldn't extend to other integrations, direct use of the global store, or other shared observable state.

And these types of cross-tree state updates may become much more common if signals are standardized in JavaScript.

## Potential solution

What could help is if all components scheduled their update tasks with a central scheduler that guaranteed that tasks would run in tree order (specifics like what window the tasks have to be scheduled in, how this interacts with the microtask or task queues, purposefully left out).

Adding this tree-aware of scheduling to the DOM would achieve a few things:

* Relieve responsibilities from frameworks and custom element libraries
* Increase interoperability between frameworks when mixed in a single tree
* Make it possible for independently built custom elements to get tree-ordered updates even with cross-tree state changes.

Additionally, a DOM scheduler seems like a good primitive to have for declarative custom elements and template instantiation. For example, the timing of when a declarative updates when it's state changes, can be defined by it calling `queueDOMUpdate()`.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/WICG/webcomponents/issues/1055
You are receiving this because you are subscribed to this thread.

Message ID: <WICG/webcomponents/issues/1055@github.com>

Received on Wednesday, 24 April 2024 17:06:33 UTC