fix: batch globalResizeObserver callbacks via requestAnimationFrame#453
fix: batch globalResizeObserver callbacks via requestAnimationFrame#453chrisnojima wants to merge 2 commits into
Conversation
Calling ResizeObserver callbacks synchronously causes "ResizeObserver loop completed with undelivered notifications" errors in browsers/Electron when a callback triggers layout changes that generate new resize observations before the current notification batch is fully delivered. Defer callback dispatch through requestAnimationFrame, coalescing all entries that arrive within the same frame into a single flush. This matches the standard pattern for ResizeObserver implementations and eliminates the loop error without changing observable behavior for callers.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: df0f9f6f71
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| pending = pending.concat(entries); | ||
| if (!ticking) { | ||
| ticking = true; | ||
| requestAnimationFrame(() => { |
There was a problem hiding this comment.
Guard ResizeObserver batching when requestAnimationFrame is absent
This callback path now assumes requestAnimationFrame exists whenever ResizeObserver exists, but createResizeObserver only feature-detects ResizeObserver. In environments that provide/polyfill ResizeObserver without a global rAF (e.g., some headless or test runtimes), the first resize notification will throw and permanently break layout syncing for observed elements. Add an rAF availability check with a fallback scheduler (like setTimeout) before enabling batching.
Useful? React with 👍 / 👎.
| globalThis.ResizeObserver = class MockResizeObserver { | ||
| constructor(cb: ResizeObserverCallback) { | ||
| capturedResizeCallback = cb; | ||
| } | ||
| observe() {} | ||
| unobserve() {} | ||
| disconnect() {} | ||
| } as unknown as typeof ResizeObserver; |
There was a problem hiding this comment.
Restore ResizeObserver global after test file setup
The test file replaces globalThis.ResizeObserver at module scope but never restores it, so later test files can inherit this mock and run against a no-op observer instead of the real/fallback implementation. Because Bun test runs files in a shared process, this creates order-dependent cross-file contamination and can hide or introduce failures outside this suite. Restore globalThis.ResizeObserver in teardown to keep tests isolated.
Useful? React with 👍 / 👎.
|
Actually, maybe this is a side effect of me doing my own paging. When I switched to |
|
Actually I think this is still a real issue. If i'm quickly switching my chat threads I can get this to happen still. |
…ications requestAnimationFrame still runs within the same ResizeObserver processing cycle, which can trigger 'ResizeObserver loop completed with undelivered notifications'. Using setTimeout(fn, 0) defers processing to the macrotask queue, ensuring the entire synchronous burst of resize events accumulates before any callback runs.
|
And after some back and forth I think a RAF isn't enough, you have to debounce it. So strangely I see this in my webpack overlay, but it won't show in console or if you breakpoint on caught/uncaught exceptions. Some prior discussions: https://stackoverflow.com/questions/76187282/how-to-fix-resizeobserver-loop-completed-with-undelivered-notifications |
Hi,
I've been using 3.0.0-beta.56 and I ran into an issue w/ my list on web (electron). If i set my drawDistance higher when going into my chat app I'd get a 'ResizeObserver loop completed with undelivered notifications' error immediately. This fixes it for me.
AI summary below: