diff --git a/.changeset/modern-towns-call.md b/.changeset/modern-towns-call.md new file mode 100644 index 0000000000..77ca8e9185 --- /dev/null +++ b/.changeset/modern-towns-call.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: discard batches made obsolete by commit diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 3a4872b0b6..b100d559d2 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -438,6 +438,8 @@ export class Batch { discard() { for (const fn of this.#discard_callbacks) fn(this); this.#discard_callbacks.clear(); + + batches.delete(this); } #commit() { @@ -466,13 +468,15 @@ export class Batch { sources.push(source); } - if (sources.length === 0) { - continue; - } - // Re-run async/block effects that depend on distinct values changed in both batches var others = [...batch.current.keys()].filter((s) => !this.current.has(s)); - if (others.length > 0) { + + if (others.length === 0) { + if (is_earlier) { + // this batch is now obsolete and can be discarded + batch.discard(); + } + } else if (sources.length > 0) { if (DEV) { invariant(batch.#roots.length === 0, 'Batch has scheduled roots'); } @@ -1095,7 +1099,6 @@ export function fork(fn) { } if (!committed && batches.has(batch)) { - batches.delete(batch); batch.discard(); } } diff --git a/packages/svelte/tests/runtime-runes/samples/async-discard-obsolete-batch/_config.js b/packages/svelte/tests/runtime-runes/samples/async-discard-obsolete-batch/_config.js new file mode 100644 index 0000000000..64e1a4b2b5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-discard-obsolete-batch/_config.js @@ -0,0 +1,89 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + await tick(); + + const [increment, shift, pop] = target.querySelectorAll('button'); + + assert.htmlEqual( + target.innerHTML, + ` + + + +

1 = 1

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

1 = 1

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

1 = 1

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

3 = 3

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

3 = 3

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

5 = 5

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-discard-obsolete-batch/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-discard-obsolete-batch/main.svelte new file mode 100644 index 0000000000..faa8d139a6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-discard-obsolete-batch/main.svelte @@ -0,0 +1,36 @@ + + + + + + + +

{n} = {await push(n)}