fix(signals): settle async iterators on empty completion and sync rej…#2870
Merged
ryansolid merged 1 commit intoJul 10, 2026
Merged
Conversation
…ection (closes solidjs#2868, closes solidjs#2869)
🦋 Changeset detectedLatest commit: c46b34e The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Merging this PR will improve performance by 25.66%
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing |
This was referenced Jul 10, 2026
2.0.0-beta.16: async iterable that completes without yielding leaves
<Loading> pending forever
#2868
Closed
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.
Closes #2868
Closes #2869
Summary
Two settlement gaps in the async-iterator branch of the client async runtime, both of which leave the node
STATUS_PENDINGforever — the<Loading>fallback never leaves and<Errored>never renders:<Loading>pending forever #2868): an async iterable that completes ({done: true}) asynchronously without ever yielding never settles. The sync completion path already commitsundefined, so a stream that legitimately produces zero items is indistinguishable from a hung request.next()thenable that callsonRejectedbefore.then()returns is dropped with no error handling. The promise branch handles the exact same thenable correctly — only the iterator branch loses it.Repros: https://stackblitz.com/edit/axbsw9wp-5do9ntby?file=src%2FApp.tsx (#2868), https://stackblitz.com/edit/3etmyz9r-pn3hs9cj?file=src%2FApp.tsx (#2869)
Root cause
Both are missing halves of the sync/async duality the promise branch handles a few lines above. The fulfillment handler settles yielded values but not an empty completion (
completed = true; schedule(); flush()— no write, no status change), and the rejection handler is guarded byif (!isSync && …), so a rejection delivered during the synchronous window is ignored entirely with nothing else recording it.Fix
Mirror the promise branch:
{done: true}arrives asynchronously and no value was ever yielded (hadSyncValuebroadened tohadValue, which now also tracks async yields), settle through the standard path withasyncWrite(undefined)— matching the immediately-done sync iterator. Completion after a yield keeps the existing schedule/flush (the last value is already committed).rejected/syncErrorduring the synchronous window, then route throughhandleError(syncError)once the window closes, exactly like the promise branch — including the rethrow that unwinds the in-progress synchronous read. The rethrow is gated to the initial read: lateriterate()calls run fromasyncWritecontinuations, where rethrowing would produce an unhandled rejection; therehandleErrorsettles the node and iteration stops.No change to the per-yield hot path beyond one boolean assignment; stale-iterator writes remain guarded by the existing
el._inFlightchecks.Solid 1.x note
Not applicable — async-iterable memo bodies are a new 2.0 API with no 1.x counterpart. Same hardening class as the server-side sync-thenable fixes in #2858, client iterator branch.
Tests
Seven new tests in
tests/syncThenable.test.ts, five red-first (confirmed failing on the unfixed source) and two passing behavior-pins:undefined(red)undefinedthrough the boundary (red; composes with 2.0.0-beta.16:Erroredfallback receives the internalStatusErrorwrapper for bare rejections #2866)Both issue repros confirmed reproducing against the published
2.0.0-beta.16npm package. Full solid-signals / solid-js / @solidjs/web suites pass.Full disclosure: I wrote this with the help of an AI assistant (Claude Fable 5) and reviewed every line before pushing. All red-first tests were confirmed failing on the unfixed code, and the bugs were verified against the published beta.16 npm package, not just this repo.