From 273f1a85a4dbe2937f2d97afa2511e828eb8ebba Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:33:02 +0200 Subject: [PATCH] fix: keep flushing new eager effects (#18102) The previous code "swallowed" new additions to the array of eager effects that happened while flushing since `eager_flush` did not clear the array before running, only afterwards. Now it clears the beforehand, causing newly added eager effects to run, too. An example where this can happen is `$state.eager` and `$effect.pending` in combination: first `$state.eager` is flushed, then due to `flushSync` the `queue_micro_task` inside `boundary.js` that flushes `$effect.pending` is triggered synchronously, adding new entries to the `eager_versions` array. If they're only cleared at the end of `eager_flush`, new entries are swallowed. Related to #18095 (but not fixing it yet) --- .changeset/sweet-boxes-unite.md | 5 +++ .../src/internal/client/reactivity/batch.js | 14 ++++---- .../async-effect-pending-eager/_config.js | 35 +++++++++++++++++++ .../async-effect-pending-eager/main.svelte | 27 ++++++++++++++ 4 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 .changeset/sweet-boxes-unite.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-pending-eager/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-pending-eager/main.svelte diff --git a/.changeset/sweet-boxes-unite.md b/.changeset/sweet-boxes-unite.md new file mode 100644 index 0000000000..f58ca436be --- /dev/null +++ b/.changeset/sweet-boxes-unite.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: keep flushing new eager effects diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 68aeb66f93..82be1d1e8d 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -1074,15 +1074,13 @@ export function schedule_effect(effect) { let eager_versions = []; function eager_flush() { - try { - flushSync(() => { - for (const version of eager_versions) { - update(version); - } - }); - } finally { + flushSync(() => { + const eager = eager_versions; eager_versions = []; - } + for (const version of eager) { + update(version); + } + }); } /** diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-pending-eager/_config.js b/packages/svelte/tests/runtime-runes/samples/async-effect-pending-eager/_config.js new file mode 100644 index 0000000000..b37b3c9228 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-pending-eager/_config.js @@ -0,0 +1,35 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + await tick(); + const [increment, shift] = target.querySelectorAll('button'); + + increment.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + + 1 +

pending: 1

+

loading...

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

pending: 0

+

1

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-pending-eager/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-pending-eager/main.svelte new file mode 100644 index 0000000000..535968a46a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-pending-eager/main.svelte @@ -0,0 +1,27 @@ + + + + + +{$state.eager(value)} + +{#if 1} +

pending: {$effect.pending()}

+ + {@const tmp = await delayed(value)} + {#if $effect.pending() > 0} +

loading...

+ {:else} +

{tmp}

+ {/if} +{/if}