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