mirror of https://github.com/sveltejs/svelte
fix: avoid erroneous async derived expressions for blocks (#17604)
fixes #17595 When an if/key/etc block has an expression that depends on an async blocker (e.g., is inside a component with top level `await`), the compiler incorrectly treats the expression as async - even when the expression itself contains no `await`. This causes the expression to be added to `$.async`'s `expressions` array, which wraps it in an `async_derived`. This is not only unnecessary but also buggy: it breaks the direct reactive connection between the source and its dependent effects, causing inconsistent effect executions. The fix is to only add expressions to `$.async`'s `expressions` array when they actually contain an `await`. When a branch is speculatively marked for destruction (condition temporarily falsy), its child effects are reset to `CLEAN` to prevent them running in a doomed branch (as of #17581). However, if the branch survives (condition becomes truthy again), those effects remain `CLEAN` and never run - the source was already marked dirty before the reset, so no new dirty marking occurs. The fix is to change `skipped_effects` from a `Set` to a `Map` that tracks which child effects were dirty/maybe_dirty before being reset. When a branch is unskipped (survives), restore their status and reschedule them. --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>pull/17614/head
parent
cd8d40af1a
commit
bc4dc1d10d
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: avoid erroneous async derived expressions for blocks
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: reschedule effects inside unskipped branches
|
||||
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
let { id } = $props();
|
||||
|
||||
// BUG: This logs 'undefined' on unmount when parent has async derived
|
||||
const data = $derived(await Promise.resolve(id).then((x) => {
|
||||
console.log('promise resolved with:', x);
|
||||
return x;
|
||||
}));
|
||||
</script>
|
||||
@ -0,0 +1,16 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target, logs }) {
|
||||
await tick();
|
||||
|
||||
assert.deepEqual(logs, ['promise resolved with:', 'some-id']);
|
||||
|
||||
const button = target.querySelector('button');
|
||||
button?.click();
|
||||
await tick();
|
||||
|
||||
assert.deepEqual(logs, ['promise resolved with:', 'some-id']);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
<script>
|
||||
import Child from './Child.svelte';
|
||||
|
||||
// This async derived in parent triggers the bug
|
||||
const something = $derived(await Promise.resolve('test'));
|
||||
|
||||
let active = $state('some-id');
|
||||
</script>
|
||||
|
||||
{#if active}
|
||||
<Child id={active} />
|
||||
{/if}
|
||||
|
||||
<button onclick={() => active = undefined}>close</button>
|
||||
Loading…
Reference in new issue