fix: ensure async deriveds always get dependencies from thennable

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.
pull/16672/head
Simon Holthausen 2 weeks ago
parent 0d48916e02
commit 5d3db10239

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure async deriveds always get dependencies from thennable

@ -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);
}

@ -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,
`
<button>increment</button>
<button>pop</button>
<p>1</p>
`
);
increment.click();
await tick();
pop.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>increment</button>
<button>pop</button>
<p>2</p>
`
);
}
});

@ -0,0 +1,35 @@
<script>
let count = $state(0);
let deferreds = [];
class X {
constructor(promise) {
this.promise = promise;
}
get then() {
count;
return (resolve) => this.promise.then(() => count).then(resolve)
}
}
function push() {
const deferred = Promise.withResolvers();
deferreds.push(deferred);
return new X(deferred.promise);
}
</script>
<button onclick={() => count += 1}>increment</button>
<button onclick={() => deferreds.pop()?.resolve(count)}>pop</button>
<svelte:boundary>
<p>{await push()}</p>
{#snippet pending()}
<p>loading...</p>
{/snippet}
</svelte:boundary>
Loading…
Cancel
Save