diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 2ae137c238..1692762ae4 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -421,8 +421,8 @@ export function update_derived(derived) { // to the previous batch, not the new one (which can already exist if an earlier // effect wrote to a source). Not adding it to either can cause bugs, so we add it to both. // See https://github.com/sveltejs/svelte/pull/18117 for more details. - current_batch?.capture(derived, value, true); previous_batch?.capture(derived, value, true); + current_batch?.capture(derived, value, true); } else { derived.v = value; derived.wv = increment_write_version(); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-previous-effect-batch/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-previous-effect-batch/_config.js new file mode 100644 index 0000000000..8f021032e0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-previous-effect-batch/_config.js @@ -0,0 +1,33 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +// Batch B (x) commits while an earlier batch A (y) is still pending, but B stays around because +// of the async work in the new boundary. While B flushes its effects, an effect creates a new batch C, +// and `d` is computed for the first time afterwards (so it's captured in both B and C). When A +// commits later, it sees B's previous values, which must be consistent (`d` must be `x * 2`). +export default test({ + async test({ assert, target, logs }) { + await tick(); + const [y, x, shift] = target.querySelectorAll('button'); + assert.deepEqual(logs, [0, 0, 0]); + + // A + y.click(); + await tick(); + + // B + x.click(); + await tick(); + assert.deepEqual(logs, [0, 0, 0, 0, 1, 2]); + + // A commits + shift.click(); + await tick(); + + for (let i = 0; i < logs.length; i += 3) { + assert.equal(logs[i + 2], logs[i + 1] * 2, `torn read: ${logs.slice(i, i + 3)}`); + } + + assert.deepEqual(logs.slice(-3), [1, 1, 2]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-previous-effect-batch/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-previous-effect-batch/main.svelte new file mode 100644 index 0000000000..5cd0876884 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-previous-effect-batch/main.svelte @@ -0,0 +1,38 @@ + + + + + + +
{y ? await push(y) : y}
+ +{#if x} +{await push(x)}
+ {#snippet pending()} +loading
+ {/snippet} +