Fix: Don't wrap await inside @const async functions with save #16996
+90
−5
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.
Problem
Since Svelte 5.39.0, awaiting inside an async function assigned to a
@consttag was broken. The component would not update/rerender when state changes occurred before the await, even though console logging showed the functions were being called correctly.Workaround: Moving the
await doSomething()before theactiveTab = tab.idfixed it, or definingswitchTabin the script section instead of inline.Root Cause
The
is_reactive_expressionfunction inAwaitExpression.jswas returningtruefor allawaitexpressions when thein_derivedflag was set. When visiting@consttag initialization expressions, the compiler setsin_derived: truefor the entire initialization, including any async functions defined within it.This caused
awaitexpressions inside those async functions to be incorrectly marked as "pickled awaits" and wrapped with$.save():The
$.save()wrapper broke the reactivity chain, preventing the UI from updating.Solution
Modified the
is_reactive_expressionfunction to properly distinguish between:awaitexpressions inside regular functions (should NOT be pickled)awaitexpressions in reactive contexts like$derived(SHOULD be pickled)When a function is found in the AST path, the fix checks if there's a reactive context (identified by nodes with
.metadataor CallExpressions to reactive runes like$derived,$effect,$inspect) between the function and theawait. Only if such a reactive context exists is theawaitconsidered reactive.This preserves correct behavior for all cases:
{@const fn = async () => { await x() }}- await not wrapped (fixed)$derived(await promise)- await wrapped (preserved)async function() { $derived(await promise) }- await wrapped (preserved)Testing
Fixes #15133
Original prompt
This section details on the original issue you should resolve
<issue_title>Regression: Awaiting inside an async function assigned to const broken since 5.39.0</issue_title>
<issue_description>### Describe the bug
Prior to 5.39.0, awaiting an async function inside a function assigned to a
@constworked without issue. From 5.39.0 onward, even though console logging shows that both the parent and child functions are getting called with the right values, the actual component doesn't seem to be updating/rerendering, as if theactiveTabassignment did nothing.Things that seem to change behavior:
await doSomething()before theactiveTab = tab.idfixes it (but in our case,doSomethingactually relies onactiveTabbeing changed first, so this doesn't work for us)switchTaba function in the script section (instead of inline) and then using<button onclick={() => switchTab(tab)}>fixed it, this is what we have done to fix things on our endReproduction
Works on 5.38.10: https://svelte.dev/playground/9edd553009364fc7b6f6031a756b7acf?version=5.38.10
Broken from 5.39.0: https://svelte.dev/playground/9edd553009364fc7b6f6031a756b7acf?version=5.39.0
Broken in latest: https://svelte.dev/playground/9edd553009364fc7b6f6031a756b7acf?version=latest
(switch from Tab 1 to 2 to 3. Working version changes text all the time, broken version gets stuck after one click)
Logs
System Info
System: OS: macOS 15.6.1 CPU: (14) arm64 Apple M4 Pro Memory: 534.53 MB / 24.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 22.15.1 - ~/.asdf/installs/nodejs/22.15.1/bin/node Yarn: 1.22.22 - ~/.asdf/installs/nodejs/22.15.1/bin/yarn npm: 10.9.2 - ~/.asdf/plugins/nodejs/shims/npm pnpm: 10.17.0 - ~/.asdf/installs/nodejs/22.15.1/bin/pnpm Browsers: Brave Browser: 140.1.82.170 Chrome: 140.0.7339.186 Safari: 18.6 npmPackages: svelte: ^5.39.3 => 5.39.3Severity
annoyance</issue_description>
<agent_instructions>This Svelte input
causes this output