From eb10a70cf1f003c3e93f3c4ecc9749870e7581c6 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 18 May 2026 17:36:17 +0200 Subject: [PATCH] fix: unset context synchronously in `run` (#18236) Our `run` function which executes top level awaits (and synchronous statements in-between/after) did not unset the context in time in case the function returns an async value. In that case the context was still around until the that promise resolves, which can be too late because unrelated things can be intertwined with the batch. The test shows this: Without the fix, the unrelated count incrementation would not update the view until the top level awaits in the child are done. In the test this just shows as a delayed visual update, but it also can result in stale roots as shown in https://github.com/sveltejs/svelte/issues/18221#issuecomment-4470921077 --- .changeset/tangy-icons-dig.md | 5 ++++ .../src/internal/client/reactivity/async.js | 23 ++++++++++------ .../async-run-isolated-batch/Child.svelte | 7 +++++ .../async-run-isolated-batch/_config.js | 26 +++++++++++++++++++ .../async-run-isolated-batch/main.svelte | 23 ++++++++++++++++ 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 .changeset/tangy-icons-dig.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/main.svelte diff --git a/.changeset/tangy-icons-dig.md b/.changeset/tangy-icons-dig.md new file mode 100644 index 0000000000..6d87912bbb --- /dev/null +++ b/.changeset/tangy-icons-dig.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: unset context synchronously in `run` diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index c1d4cbcd67..12c5e9baa5 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -303,15 +303,21 @@ export function run(thunks) { .then(() => { restore(); - if (errored) { - throw errored.error; + try { + if (errored) { + throw errored.error; + } + + if (aborted(active)) { + throw STALE_REACTION; + } + + return fn(); + } finally { + // We gotta unset context directly in case the function returns a promise, in which case + // unset_context in .finally() would be too late ... + unset_context(); } - - if (aborted(active)) { - throw STALE_REACTION; - } - - return fn(); }) .catch(handle_error); @@ -320,6 +326,7 @@ export function run(thunks) { promise.finally(() => { blocker.settled = true; + // ... but we also need it after such a promise has resolved in case it restores our context unset_context(); }); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/Child.svelte new file mode 100644 index 0000000000..6f84dc319e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/Child.svelte @@ -0,0 +1,7 @@ + + +{x} {y} diff --git a/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/_config.js b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/_config.js new file mode 100644 index 0000000000..a0dc38c660 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/_config.js @@ -0,0 +1,26 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [show, resolve, count] = target.querySelectorAll('button'); + + show.click(); + await tick(); + resolve.click(); + await tick(); + count.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ' ' + ); + + resolve.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + '1 2 ' + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/main.svelte new file mode 100644 index 0000000000..0a5346cc47 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/main.svelte @@ -0,0 +1,23 @@ + + + + +{#if show} + +{/if} + + + +