diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index f2ed02a23e..c39184427f 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -37,7 +37,8 @@ import { eager_block_effects, schedule_effect, legacy_updates, - current_batch + current_batch, + first_batch } from './batch.js'; import { proxy } from '../proxy.js'; import { execute_derived } from './deriveds.js'; @@ -206,7 +207,11 @@ export function internal_set(source, value, updated_during_traversal = null) { // During time traveling we don't want to reset the status so that // traversal of the graph in the other batches still happens - if (batch_values === null) { + if ( + batch_values === null && + // could also be "read outside of reactivity", e.g. in an event handler + !first_batch?.next + ) { update_derived_status(derived); } } diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/_config.js new file mode 100644 index 0000000000..ae9ef996f6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/_config.js @@ -0,0 +1,39 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +const buttons = + ''; + +export default test({ + mode: ['client'], + async test({ assert, target, logs }) { + await tick(); + const [s, u, d, resolve_a, resolve_b, resolve_c] = target.querySelectorAll('button'); + + // batch A: `d` becomes 10, but is not evaluated yet (the `` is deferred) + s.click(); + await tick(); + + // batch B + u.click(); + await tick(); + + // batch C overrides `d` while A and B are pending. This must not mark `d` as + // clean, because the other batches haven't seen its new value (10) yet + d.click(); + await tick(); + + // B sees A's change (but not C's), so `d` is 10 in its world + resolve_b.click(); + await tick(); + assert.equal(logs[0], 'probe 10'); + + resolve_a.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

1

0

1010`); + + resolve_c.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

1

1

9999`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/main.svelte new file mode 100644 index 0000000000..08c00a4911 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/main.svelte @@ -0,0 +1,42 @@ + + + + + + + + + +

{await delay('a', s)}

+

{await delay('c', w)}

+ +{#if show} + {d} +{/if} + +{#if await delay('b', u)} + {await probe(d)} +{/if}