From 29fa0b40e253d56c05d03f1d9ec06b82aa0c89ab Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 23 Oct 2025 18:18:38 +0200 Subject: [PATCH] do similar thing for mark_effects, may help with #16990 --- .../src/internal/client/reactivity/batch.js | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 91635bd5d2..2ea1ed05bf 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -377,8 +377,10 @@ export class Batch { // Re-run async/block effects that depend on distinct values changed in both batches const others = [...batch.current.keys()].filter((s) => !this.current.has(s)); if (others.length > 0) { + const marked = new Set(); + const checked = new Map(); for (const source of sources) { - mark_effects(source, others); + mark_effects(source, others, marked, checked); } if (queued_root_effects.length > 0) { @@ -688,15 +690,24 @@ function flush_queued_effects(effects) { * these effects can re-run after another batch has been committed * @param {Value} value * @param {Source[]} sources + * @param {Set} marked + * @param {Map} checked */ -function mark_effects(value, sources) { +function mark_effects(value, sources, marked, checked) { + if (marked.has(value)) return; + marked.add(value); + if (value.reactions !== null) { for (const reaction of value.reactions) { const flags = reaction.f; if ((flags & DERIVED) !== 0) { - mark_effects(/** @type {Derived} */ (reaction), sources); - } else if ((flags & (ASYNC | BLOCK_EFFECT)) !== 0 && depends_on(reaction, sources)) { + mark_effects(/** @type {Derived} */ (reaction), sources, marked, checked); + } else if ( + (flags & (ASYNC | BLOCK_EFFECT)) !== 0 && + (flags & DIRTY) === 0 && // we may have scheduled this one already + depends_on(reaction, sources, checked) + ) { set_signal_status(reaction, DIRTY); schedule_effect(/** @type {Effect} */ (reaction)); } @@ -707,20 +718,27 @@ function mark_effects(value, sources) { /** * @param {Reaction} reaction * @param {Source[]} sources + * @param {Map} checked */ -function depends_on(reaction, sources) { +function depends_on(reaction, sources, checked) { + const depends = checked.get(reaction); + if (depends !== undefined) return depends; + if (reaction.deps !== null) { for (const dep of reaction.deps) { if (sources.includes(dep)) { return true; } - if ((dep.f & DERIVED) !== 0 && depends_on(/** @type {Derived} */ (dep), sources)) { + if ((dep.f & DERIVED) !== 0 && depends_on(/** @type {Derived} */ (dep), sources, checked)) { + checked.set(/** @type {Derived} */ (dep), true); return true; } } } + checked.set(reaction, false); + return false; }