From 677af5723c1a80ccfdbab81cb7ef57edeecf0201 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 26 Aug 2025 00:08:07 +0200 Subject: [PATCH] fix: ensure async deriveds always get dependencies from thennable (#16672) When an async derived already has a previous promise that is still pending, we were not accessing the `then` property of the new promise. If that property access causes signals to be read, that meant that those dependencies were lost and as such the derived wouldn't rerun anymore when it should. The fix is to make sure to always access the thennable. --- .changeset/orange-chefs-float.md | 5 +++ .../internal/client/reactivity/deriveds.js | 3 ++ .../async-derived-reverse-order/_config.js | 41 +++++++++++++++++++ .../async-derived-reverse-order/main.svelte | 35 ++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 .changeset/orange-chefs-float.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-reverse-order/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-reverse-order/main.svelte diff --git a/.changeset/orange-chefs-float.md b/.changeset/orange-chefs-float.md new file mode 100644 index 0000000000..fc5db3c680 --- /dev/null +++ b/.changeset/orange-chefs-float.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure async deriveds always get dependencies from thennable diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 7f730e365e..31dc267960 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -120,6 +120,9 @@ export function async_derived(fn, location) { try { var p = fn(); + // Make sure to always access the then property to read any signals + // it might access, so that we track them as dependencies. + if (prev) Promise.resolve(p).catch(() => {}); // avoid unhandled rejection } catch (error) { p = Promise.reject(error); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-reverse-order/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-reverse-order/_config.js new file mode 100644 index 0000000000..bd0dd753c2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-reverse-order/_config.js @@ -0,0 +1,41 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [increment, pop] = target.querySelectorAll('button'); + + increment.click(); + await tick(); + + pop.click(); + await tick(); + + pop.click(); + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` + + +

1

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

2

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-reverse-order/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-reverse-order/main.svelte new file mode 100644 index 0000000000..b9f6c26c2a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-reverse-order/main.svelte @@ -0,0 +1,35 @@ + + + + + + + +

{await push()}

+ + {#snippet pending()} +

loading...

+ {/snippet} +