From 4e0c795ba72c402a1b73a24ae31fa0e331fd0722 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 25 Aug 2025 12:29:06 -0400 Subject: [PATCH 1/5] bring tests over from #16621 --- .../samples/async-nested-top-level/Bar.svelte | 7 ++++ .../samples/async-nested-top-level/Foo.svelte | 10 +++++ .../samples/async-nested-top-level/_config.js | 42 +++++++++++++++++++ .../async-nested-top-level/main.svelte | 31 ++++++++++++++ .../samples/async-redirect/_config.js | 2 + .../samples/async-redirect/main.svelte | 4 ++ .../async-top-level-deriveds/Foo.svelte | 8 ++++ .../async-top-level-deriveds/_config.js | 41 ++++++++++++++++++ .../async-top-level-deriveds/main.svelte | 31 ++++++++++++++ 9 files changed, 176 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/async-nested-top-level/Bar.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-nested-top-level/Foo.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-nested-top-level/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-nested-top-level/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/Foo.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/Bar.svelte b/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/Bar.svelte new file mode 100644 index 000000000000..f1ac9ab760cf --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/Bar.svelte @@ -0,0 +1,7 @@ + + +

bar: {bar}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/Foo.svelte b/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/Foo.svelte new file mode 100644 index 000000000000..e2029a3033ed --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/Foo.svelte @@ -0,0 +1,10 @@ + + +

foo: {foo}

+ + diff --git a/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/_config.js b/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/_config.js new file mode 100644 index 000000000000..ca7965bf79e0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/_config.js @@ -0,0 +1,42 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [show, resolve] = target.querySelectorAll('button'); + + show.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

pending...

+ ` + ); + + resolve.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

pending...

+ ` + ); + + resolve.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

foo: foo

+

bar: bar

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/main.svelte new file mode 100644 index 000000000000..bd0efaa4f85c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-nested-top-level/main.svelte @@ -0,0 +1,31 @@ + + + + + + + + + + {#if show} + + {/if} + + {#if $effect.pending()} +

pending...

+ {/if} + + {#snippet pending()} +

initializing...

+ {/snippet} +
diff --git a/packages/svelte/tests/runtime-runes/samples/async-redirect/_config.js b/packages/svelte/tests/runtime-runes/samples/async-redirect/_config.js index ebbe642860d0..fe92977c21f4 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-redirect/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/async-redirect/_config.js @@ -29,6 +29,7 @@ export default test({

c

+

b or c

` ); @@ -46,6 +47,7 @@ export default test({

b

+

b or c

` ); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-redirect/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-redirect/main.svelte index bf5fdf9ed395..aead1b00e597 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-redirect/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/async-redirect/main.svelte @@ -33,6 +33,10 @@

c

{/if} + {#if route === 'b' || route === 'c'} +

b or c

+ {/if} + {#snippet pending()}

pending...

{/snippet} diff --git a/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/Foo.svelte b/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/Foo.svelte new file mode 100644 index 000000000000..e8a7c8413748 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/Foo.svelte @@ -0,0 +1,8 @@ + + +

{foo} {bar}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/_config.js b/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/_config.js new file mode 100644 index 000000000000..2c7ffd395265 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/_config.js @@ -0,0 +1,41 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [show, resolve] = target.querySelectorAll('button'); + + show.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

pending...

+ ` + ); + + resolve.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

pending...

+ ` + ); + + resolve.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

foo bar

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/main.svelte new file mode 100644 index 000000000000..bd0efaa4f85c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-top-level-deriveds/main.svelte @@ -0,0 +1,31 @@ + + + + + + + + + + {#if show} + + {/if} + + {#if $effect.pending()} +

pending...

+ {/if} + + {#snippet pending()} +

initializing...

+ {/snippet} +
From e0c90fbc7daec7e105cca1149bb3699424a9405e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 25 Aug 2025 12:36:51 -0400 Subject: [PATCH 2/5] fix, albeit one i don't fully understand --- packages/svelte/src/internal/client/reactivity/async.js | 6 ++++-- packages/svelte/src/internal/client/reactivity/batch.js | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index 1ea1bbe56160..65d004137fcb 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -73,11 +73,13 @@ function capture() { var previous_effect = active_effect; var previous_reaction = active_reaction; var previous_component_context = component_context; + var previous_batch = current_batch; return function restore() { set_active_effect(previous_effect); set_active_reaction(previous_reaction); set_component_context(previous_component_context); + previous_batch?.activate(); if (DEV) { set_from_async_derived(null); @@ -176,8 +178,8 @@ export function unset_context() { * @param {() => Promise} fn */ export async function async_body(fn) { - const unsuspend = suspend(); - const active = /** @type {Effect} */ (active_effect); + var unsuspend = suspend(); + var active = /** @type {Effect} */ (active_effect); try { await fn(); diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 60fa03c56cd2..d115c40df738 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -680,6 +680,10 @@ export function suspend() { batch.decrement(); } + if (batch === current_batch) { + batch.deactivate(); + } + unset_context(); }; } From a9912aa2c1bac008df43f0fd5ebc3b4d7db83902 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 25 Aug 2025 12:41:11 -0400 Subject: [PATCH 3/5] tweak --- packages/svelte/src/internal/client/reactivity/batch.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index d115c40df738..568061d23fdf 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -76,8 +76,8 @@ let queued_root_effects = []; let last_scheduled_effect = null; let is_flushing = false; - let is_flushing_sync = false; + export class Batch { /** * The current values of any sources that are updated in this batch @@ -678,9 +678,7 @@ export function suspend() { if (!pending) { batch.activate(); batch.decrement(); - } - - if (batch === current_batch) { + } else if (batch === current_batch) { batch.deactivate(); } From d18c7192d53a71363283c01a659261bfb10c2c41 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 25 Aug 2025 12:41:43 -0400 Subject: [PATCH 4/5] changeset --- .changeset/silent-pigs-relax.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silent-pigs-relax.md diff --git a/.changeset/silent-pigs-relax.md b/.changeset/silent-pigs-relax.md new file mode 100644 index 000000000000..5acf185ffedf --- /dev/null +++ b/.changeset/silent-pigs-relax.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: restore batch along with effect context From 5769547e50e85e930c1205179e4732b750c275fb Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 25 Aug 2025 12:48:56 -0400 Subject: [PATCH 5/5] deactivate on unsuspend --- packages/svelte/src/internal/client/reactivity/batch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 568061d23fdf..2c60fc8313b6 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -678,7 +678,7 @@ export function suspend() { if (!pending) { batch.activate(); batch.decrement(); - } else if (batch === current_batch) { + } else { batch.deactivate(); }