diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 88e8caae56..ffcf2d4fd1 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -112,11 +112,8 @@ export class Boundary { #pending_count = 0; #pending_count_update_queued = false; - /** @type {Set} */ - #dirty_effects = new Set(); - - /** @type {Set} */ - #maybe_dirty_effects = new Set(); + /** @type {Map} */ + #dirty_reactions = new Map(); /** * A source containing the number of pending async deriveds/expressions. @@ -358,7 +355,7 @@ export class Boundary { // any effects that were previously deferred should be transferred // to the batch, which will flush in the next microtask - batch.transfer_effects(this.#dirty_effects, this.#maybe_dirty_effects); + batch.transfer_reactions(this.#dirty_reactions); } /** @@ -366,7 +363,7 @@ export class Boundary { * @param {Effect} effect */ defer_effect(effect) { - defer_effect(effect, this.#dirty_effects, this.#maybe_dirty_effects); + defer_effect(effect, this.#dirty_reactions); } /** diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 05f7586072..3ebeb519d0 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -177,16 +177,10 @@ export class Batch { #new_effects = []; /** - * Deferred effects (which run after async work has completed) that are DIRTY - * @type {Set} - */ - #dirty_effects = new Set(); - - /** - * Deferred effects that are MAYBE_DIRTY - * @type {Set} + * Deferred effects (which run after async work has completed) and their status (DIRTY or MAYBE_DIRTY) + * @type {Map} */ - #maybe_dirty_effects = new Set(); + #dirty_reactions = new Map(); /** * A map of branches that still exist, but will be destroyed when this batch @@ -341,14 +335,8 @@ export class Batch { // #is_deferred() is true, because traversing the tree could make // an if block that contains the last blocking pending effect falsy, // causing the block to no longer be deferred. - for (const e of this.#dirty_effects) { - this.#maybe_dirty_effects.delete(e); - set_signal_status(e, DIRTY); - this.schedule(e); - } - - for (const e of this.#maybe_dirty_effects) { - set_signal_status(e, MAYBE_DIRTY); + for (const [e, status] of this.#dirty_reactions) { + set_signal_status(e, status); this.schedule(e); } @@ -432,8 +420,7 @@ export class Batch { } // clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches. - this.#dirty_effects.clear(); - this.#maybe_dirty_effects.clear(); + this.#dirty_reactions.clear(); // append/remove branches for (const fn of this.#commit_callbacks) fn(this); @@ -510,7 +497,7 @@ export class Batch { } else if (async_mode_flag && (flags & (RENDER_EFFECT | MANAGED_EFFECT)) !== 0) { render_effects.push(effect); } else if (is_dirty(effect)) { - if ((flags & BLOCK_EFFECT) !== 0) this.#maybe_dirty_effects.add(effect); + if ((flags & BLOCK_EFFECT) !== 0) this.add_dirty_reaction(effect, MAYBE_DIRTY); update_effect(effect); } @@ -576,7 +563,7 @@ export class Batch { batch.async_deriveds.clear(); // Mark is not guaranteed not touch these, so we transfer them - this.transfer_effects(batch.#dirty_effects, batch.#maybe_dirty_effects); + this.transfer_reactions(batch.#dirty_reactions); /** * mark all effects that depend on `batch.current`, except the @@ -605,7 +592,7 @@ export class Batch { var effect = /** @type {Effect} */ (reaction); if (flags & (ASYNC | BLOCK_EFFECT) && !this.async_deriveds.has(effect)) { - this.#maybe_dirty_effects.delete(effect); + this.#dirty_reactions.delete(effect); set_signal_status(effect, DIRTY); this.schedule(effect); } @@ -629,7 +616,7 @@ export class Batch { */ #defer_effects(effects) { for (var i = 0; i < effects.length; i += 1) { - defer_effect(effects[i], this.#dirty_effects, this.#maybe_dirty_effects); + defer_effect(effects[i], this.#dirty_reactions); } } @@ -818,7 +805,7 @@ export class Batch { set_signal_status(effect, DIRTY); batch.schedule(effect); } else { - batch.#dirty_effects.add(effect); + batch.add_dirty_reaction(effect, DIRTY); } } } @@ -882,20 +869,24 @@ export class Batch { } /** - * @param {Set} dirty_effects - * @param {Set} maybe_dirty_effects + * @param {Map} dirty_reactions */ - transfer_effects(dirty_effects, maybe_dirty_effects) { - for (const e of dirty_effects) { - this.#dirty_effects.add(e); + transfer_reactions(dirty_reactions) { + for (const [effect, status] of dirty_reactions) { + this.add_dirty_reaction(effect, status); } - for (const e of maybe_dirty_effects) { - this.#maybe_dirty_effects.add(e); - } + dirty_reactions.clear(); + } - dirty_effects.clear(); - maybe_dirty_effects.clear(); + /** + * @param {Effect} effect + * @param {number} status + */ + add_dirty_reaction(effect, status) { + if (status === DIRTY || this.#dirty_reactions.get(effect) !== DIRTY) { + this.#dirty_reactions.set(effect, status); + } } /** @param {(batch: Batch) => void} fn */ diff --git a/packages/svelte/src/internal/client/reactivity/utils.js b/packages/svelte/src/internal/client/reactivity/utils.js index 3280f2372e..edd874b71c 100644 --- a/packages/svelte/src/internal/client/reactivity/utils.js +++ b/packages/svelte/src/internal/client/reactivity/utils.js @@ -4,14 +4,13 @@ import { set_signal_status } from './status.js'; /** * @param {Effect} effect - * @param {Set} dirty_effects - * @param {Set} maybe_dirty_effects + * @param {Map} dirty_reactions */ -export function defer_effect(effect, dirty_effects, maybe_dirty_effects) { +export function defer_effect(effect, dirty_reactions) { if ((effect.f & DIRTY) !== 0) { - dirty_effects.add(effect); - } else if ((effect.f & MAYBE_DIRTY) !== 0) { - maybe_dirty_effects.add(effect); + dirty_reactions.set(effect, DIRTY); + } else if ((effect.f & MAYBE_DIRTY) !== 0 && dirty_reactions.get(effect) !== DIRTY) { + dirty_reactions.set(effect, MAYBE_DIRTY); } // mark as clean so they get scheduled if they depend on pending async state