diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index f7729c75f2..b32b51fb5c 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -129,6 +129,20 @@ export class Batch { */ merged_into = null; + /** + * @type {Map | null} + */ + #stale_effects = null; + + /** + * Effects that were executed with stale values. Its wv is written into the map. + * Lazily initialized for performance reasons. + * @type {Map} + */ + get stale_effects() { + return (this.#stale_effects ??= new Map()); + } + /** @type {Batch | null} */ prev = null; @@ -958,6 +972,10 @@ export class Batch { wv_values.set(source, current.wv); } + for (const [effect, wv] of this.#stale_effects ?? []) { + wv_values.set(effect, wv); + } + for (let batch = first_batch; batch !== null; batch = batch.next) { if (batch === this) continue; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index fef09dd11c..8fffee8c47 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -490,16 +490,25 @@ export function update_effect(effect) { // Did the effect see the latest value of all its dependencies, or (partly) its batch's view // of them, i.e. did `batch_values` hand it a value that differs from the real one? We only // need to look one level deep: a derived that was itself computed from such a value was not - // written to the real world either, so it differs as well. If it saw its batch's view, we - // don't update its write version. + // written to the real world either, so it differs as well. + var own_batch = previous_batch ?? current_batch; // can be null inside flush_eager_effects var is_latest_value = - (previous_batch === null && current_batch === null) || // e.g. inside flush_eager_effects + own_batch === null || batch_values === null || effect.deps === null || !effect.deps.some((d) => batch_values?.has(d) && batch_values.get(d) !== d.v); if (is_latest_value) { effect.wv = write_version; + } else { + // The effect ran with values that are not the latest ones (it saw its own batch's view). + // Don't update its write version — instead remember it so that the batch can bring it + // up to date on commit, and tell all subsequent batches that it may need to re-run in their view. + var own = /** @type {Batch} */ (own_batch); + own.stale_effects.set(effect, write_version); + for (var batch = own.next; batch !== null; batch = batch.next) { + batch.add_dirty_reaction(effect, MAYBE_DIRTY); + } } // In DEV, increment versions of any sources that were written to during the effect,