From 66396f391ee109af8420882dd89be658ac6ce9d2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 14 Aug 2025 14:28:16 -0400 Subject: [PATCH] fix: only abort effect flushing if it causes an existing effect to be scheduled --- .changeset/clever-months-clap.md | 5 +++++ .../svelte/src/internal/client/reactivity/batch.js | 14 +++++--------- .../src/internal/client/reactivity/sources.js | 6 ++++++ 3 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 .changeset/clever-months-clap.md diff --git a/.changeset/clever-months-clap.md b/.changeset/clever-months-clap.md new file mode 100644 index 0000000000..9566dcf54d --- /dev/null +++ b/.changeset/clever-months-clap.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: only abort effect flushing if it causes an existing effect to be scheduled diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 123bc95d16..f4ba4b54b6 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -28,7 +28,7 @@ import * as e from '../errors.js'; import { flush_tasks } from '../dom/task.js'; import { DEV } from 'esm-env'; import { invoke_error_boundary } from '../error-handling.js'; -import { old_values } from './sources.js'; +import { old_values, schedule_version } from './sources.js'; import { unlink_effect } from './effects.js'; import { unset_context } from './async.js'; @@ -598,7 +598,7 @@ function flush_queued_effects(effects) { var effect = effects[i++]; if ((effect.f & (DESTROYED | INERT)) === 0 && is_dirty(effect)) { - var n = current_batch ? current_batch.current.size : 0; + var sv = schedule_version; update_effect(effect); @@ -619,13 +619,9 @@ function flush_queued_effects(effects) { } } - // if state is written in a user effect, abort and re-schedule, lest we run - // effects that should be removed as a result of the state change - if ( - current_batch !== null && - current_batch.current.size > n && - (effect.f & USER_EFFECT) !== 0 - ) { + // if an effect is invalidated by a user effect, abort and re-schedule, lest we + // run effects that should be removed as a result of the state change + if (schedule_version > sv && (effect.f & USER_EFFECT) !== 0) { break; } } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 7fb3135708..d2a57e1acf 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -299,6 +299,11 @@ export function increment(source) { set(source, source.v + 1); } +/** + * We increment this value when an effect is scheduled as a result of a state change + */ +export let schedule_version = 0; + /** * @param {Value} signal * @param {number} status should be DIRTY or MAYBE_DIRTY @@ -334,6 +339,7 @@ function mark_reactions(signal, status) { if ((flags & DERIVED) !== 0) { mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY); } else if (not_dirty) { + schedule_version += 1; schedule_effect(/** @type {Effect} */ (reaction)); } }