Per-tick locale memoization, error isolation in dateObserver, sync connectedCallback - #369
Draft
mattcosta7 with Copilot wants to merge 2 commits into
Draft
Conversation
Copilot
AI
changed the title
[WIP] Remove deferred rendering in connectedCallback
Per-tick locale memoization, error isolation in dateObserver, sync connectedCallback
Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The prior PR batched
connectedCallbackinto a microtask flush to reduce startup work, but the microtask still ran before paint — work was moved, not removed — while silently breaking the synchronous-render contract consumers rely on. This PR removes that batching entirely and ships two targeted improvements with no observable behavior change.Changes
Reverted: batching infrastructure removed
pendingElements,pendingFlush, andflushPending()connectedCallback()callsthis.update()synchronously again (as onmain)disconnectedCallback()back to onlydateObserver.unobserve(this)pendingElements.has(this)short-circuit fromattributeChangedCallbackPer-tick locale/timeZone/hourCycle memoization
During a
dateObserver.update()pass, each element's#lang/timeZone/hourCyclegetters performed an independentclosest()ancestor walk — O(N) walks for what is almost always the same value (inherited from<html>). This PR adds a cache that lives only for one synchronous pass:Cache key:
parentNode. Siblings under the same parent share an identical ancestor chain for inherited attributes, so one walk per unique parent is sufficient. Elements withlang,time-zone, orhour-cycleset directly on themselves are excluded (their own attribute overrides the inherited value).intlLocale(lang).toString()is additionally memoized in a persistentlocaleNormCachesince locale-string normalization is stable.Error isolation in
dateObserver.update()One element throwing no longer aborts the rest of the pass. Errors are rethrown via
setTimeout(() => { throw error })to preserve global error visibility without breaking the batch.attributeChangedCallbackcoalescing — no change neededThe existing
#updatingguard already coalesces multiple attribute changes within the same task into a single deferred render. No module-level queue reintroduced.Tests
shadowRoot.textContentandtitleare populated immediately afterappendChildwith noawaitlangancestors renders each with correct, independent output (proves cache does not over-share across parents)time-zonechange is reflected on the nextupdate()call (cache does not persist across passes)