restore capturing deriveds in previous_batch before current_batch

The committing batch needs the correct previous value, and the new batch must record that its
value came from the committing batch's world.
async-another-try-pt-1-prune
Rich Harris 2 days ago
parent 498a696ac8
commit 70bb7f4295
No known key found for this signature in database

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

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

@ -0,0 +1,38 @@
<script>
import { untrack } from 'svelte';
let x = $state(0);
let y = $state(0);
let z = $state(0);
let d = $derived(x * 2);
const queue = [];
function push(v) {
return new Promise((r) => queue.push(() => r(v)));
}
// creates a new batch while the batch that changed `x` flushes its effects...
$effect(() => {
if (x) untrack(() => z++);
});
// ...before `d` is computed for the first time in that batch
$effect(() => {
console.log(y, x, d);
});
</script>
<button onclick={() => y++}>y</button>
<button onclick={() => x++}>x</button>
<button onclick={() => queue.shift()()}>shift</button>
<p>{y ? await push(y) : y}</p>
{#if x}
<svelte:boundary>
<p>{await push(x)}</p>
{#snippet pending()}
<p>loading</p>
{/snippet}
</svelte:boundary>
{/if}
Loading…
Cancel
Save