diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index e73618c026..59edfda5f9 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -87,6 +87,12 @@ export class Batch { */ #deferred = null; + /** + * True if an async effect inside this batch resolved and + * its parent branch was already deleted + */ + #neutered = false; + /** * Async effects (created inside `async_derived`) encountered during processing. * These run after the rest of the batch has updated, since they should @@ -278,10 +284,14 @@ export class Batch { current_batch = null; } + neuter() { + this.#neutered = true; + } + flush() { if (queued_root_effects.length > 0) { this.flush_effects(); - } else { + } else if (!this.#neutered) { this.#commit(); } diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 58a22194d7..2012bb81ac 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -9,7 +9,8 @@ import { EFFECT_PRESERVED, MAYBE_DIRTY, STALE_REACTION, - UNOWNED + UNOWNED, + DESTROYED } from '#client/constants'; import { active_reaction, @@ -144,6 +145,10 @@ export function async_derived(fn, location) { const handler = (value, error = undefined) => { prev = null; + if ((parent.f & DESTROYED) !== 0) { + batch.neuter(); + } + current_async_effect = null; if (!pending) batch.activate(); diff --git a/packages/svelte/tests/runtime-runes/samples/async-stale-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/async-stale-derived/_config.js new file mode 100644 index 0000000000..884b27d865 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-stale-derived/_config.js @@ -0,0 +1,52 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + await tick(); + + const [increment, shift] = target.querySelectorAll('button'); + + assert.htmlEqual( + target.innerHTML, + ` + + +

0

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

2

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

delayed: 3

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-stale-derived/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-stale-derived/main.svelte new file mode 100644 index 0000000000..eeefffbee6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-stale-derived/main.svelte @@ -0,0 +1,26 @@ + + + + + + + {#if count % 2} +

delayed: {await push()}

+ {:else} +

{await count}

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

loading...

+ {/snippet} +