diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index dfa53788f3..d1f3ce08af 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -344,26 +344,28 @@ export class Batch { for (const [e, t] of this.#skipped_branches) { reset_branch(e, t); } - } else { - if (this.#pending === 0) { - batches.delete(this); + + if (updates.length > 0) { + /** @type {Batch} */ (/** @type {unknown} */ (current_batch)).#process(); } - // clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches. - this.#dirty_effects.clear(); - this.#maybe_dirty_effects.clear(); + return; + } - // append/remove branches - for (const fn of this.#commit_callbacks) fn(this); - this.#commit_callbacks.clear(); + // clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches. + this.#dirty_effects.clear(); + this.#maybe_dirty_effects.clear(); - previous_batch = this; - flush_queued_effects(render_effects); - flush_queued_effects(effects); - previous_batch = null; + // append/remove branches + for (const fn of this.#commit_callbacks) fn(this); + this.#commit_callbacks.clear(); - this.#deferred?.resolve(); - } + previous_batch = this; + flush_queued_effects(render_effects); + flush_queued_effects(effects); + previous_batch = null; + + this.#deferred?.resolve(); var next_batch = /** @type {Batch | null} */ (/** @type {unknown} */ (current_batch)); @@ -371,7 +373,7 @@ export class Batch { // else we could start flushing a new batch and then, if it has pending work, rebase it right afterwards, which is wrong. // In sync mode flushSync can cause #commit to wrongfully think that there needs to be a rebase, so we only do it in async mode // TODO fix the underlying cause, otherwise this will likely regress when non-async mode is removed - if (async_mode_flag && !batches.has(this)) { + if (async_mode_flag && this.#pending === 0) { this.#commit(); // Rebases can activate other batches or null it out, therefore restore the new one here current_batch = next_batch; @@ -530,6 +532,8 @@ export class Batch { } #commit() { + batches.delete(this); + // If there are other pending batches, they now need to be 'rebased' — // in other words, we re-run block/async effects with the newly // committed state, unless the batch in question has a more diff --git a/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after-2/_config.js b/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after-2/_config.js new file mode 100644 index 0000000000..d7293f9b70 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after-2/_config.js @@ -0,0 +1,54 @@ +import { tick } from 'svelte'; +import { ok, test } from '../../test'; + +// Test that the store is unsubscribed from, even if it's not referenced once the store itself is set to null +export default test({ + skip_no_async: true, + + async test({ target, assert }) { + assert.htmlEqual( + target.innerHTML, + `

0

` + ); + + target.querySelector('button')?.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + `

1

hello 1 ` + ); + + const input = target.querySelector('input'); + ok(input); + + input.stepUp(); + input.dispatchEvent(new Event('input', { bubbles: true })); + await tick(); + assert.htmlEqual( + target.innerHTML, + `

2

hello 2 ` + ); + + target.querySelector('button')?.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + `

2

` + ); + + input.stepUp(); + input.dispatchEvent(new Event('input', { bubbles: true })); + await tick(); + assert.htmlEqual( + target.innerHTML, + `

2

` + ); + + target.querySelector('button')?.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + `

3

hello 3 ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after-2/main.svelte new file mode 100644 index 0000000000..4abfd3c2f9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after-2/main.svelte @@ -0,0 +1,29 @@ + + + +

{count}

+ +{#if watcherA} + + {#if true} + {await 'hello'} + {/if} + + {$watcherA} + +{:else} + +{/if}