From 0f4d0b101b2d20247bb0a5167b6f6614e83af561 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 15 Aug 2025 16:46:46 +0200 Subject: [PATCH] fix --- .../internal/client/dom/blocks/boundary.js | 13 ++++++-- .../src/internal/client/reactivity/batch.js | 31 +++++++++++++++++-- .../internal/client/reactivity/deriveds.js | 5 ++- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index b7fd9a856c..350a417b8e 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -28,7 +28,7 @@ import { queue_micro_task } from '../task.js'; import * as e from '../../errors.js'; import * as w from '../../warnings.js'; import { DEV } from 'esm-env'; -import { Batch, effect_pending_updates } from '../../reactivity/batch.js'; +import { Batch, current_batch, effect_pending_updates } from '../../reactivity/batch.js'; import { internal_set, source } from '../../reactivity/sources.js'; import { tag } from '../../dev/tracing.js'; import { createSubscriber } from '../../../../reactivity/create-subscriber.js'; @@ -69,7 +69,7 @@ export class Boundary { * * @type {Batch | null} */ - batch = null; + #batch = null; /** @type {TemplateNode} */ #anchor; @@ -200,6 +200,13 @@ export class Boundary { return !!this.#props.pending; } + get_batch() { + if (current_batch) { + this.#batch = current_batch; + } + return /** @type {Batch} */ (this.#batch); + } + /** * @param {() => Effect | null} fn */ @@ -243,7 +250,7 @@ export class Boundary { if (this.#pending_count === 0) { this.pending = false; - this.batch = null; + this.#batch = null; if (this.#pending_effect) { pause_effect(this.#pending_effect, () => { diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index b3a3bef731..d102355071 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -184,6 +184,14 @@ export class Batch { /** @type {Map | null} */ var current_values = null; + /** + * A batch is superseeded if all of its sources are also in the current batch. + * If the current batch commits, we don't need the old batch anymore. + * This also prevents memory leaks since the old batch will never be committed. + * @type {Batch[]} + */ + var superseeded_batches = []; + // if there are multiple batches, we are 'time travelling' — // we need to undo the changes belonging to any batch // other than the current one @@ -196,15 +204,25 @@ export class Batch { source.v = current; } + let is_prior_batch = true; + for (const batch of batches) { - if (batch === this) continue; + if (batch === this) { + is_prior_batch = false; + continue; + } + + let superseeded = is_prior_batch; for (const [source, previous] of batch.#previous) { if (!current_values.has(source)) { + superseeded = false; current_values.set(source, { v: source.v, wv: source.wv }); source.v = previous; } } + + if (superseeded) superseeded_batches.push(batch); } } @@ -215,6 +233,10 @@ export class Batch { // if we didn't start any new async work, and no async work // is outstanding from a previous flush, commit if (this.#async_effects.length === 0 && this.#pending === 0) { + for (const batch of superseeded_batches) { + batch.remove(); + } + this.#commit(); var render_effects = this.#render_effects; @@ -376,6 +398,11 @@ export class Batch { this.#neutered = true; } + remove() { + this.neuter(); + batches.delete(this); + } + flush() { if (queued_root_effects.length > 0) { flush_effects(); @@ -664,7 +691,7 @@ export function schedule_effect(signal) { export function suspend() { var boundary = get_pending_boundary(); - var batch = (boundary.batch ??= /** @type {Batch} */ (current_batch)); + var batch = boundary.get_batch(); var pending = boundary.pending; boundary.update_pending_count(1); diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 0ac8582933..8e5d75443d 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -1,5 +1,4 @@ /** @import { Derived, Effect, Source } from '#client' */ -/** @import { Batch } from './batch.js'; */ import { DEV } from 'esm-env'; import { ERROR_VALUE, @@ -33,7 +32,7 @@ import { tracing_mode_flag } from '../../flags/index.js'; import { Boundary } from '../dom/blocks/boundary.js'; import { component_context } from '../context.js'; import { UNINITIALIZED } from '../../../constants.js'; -import { batch_deriveds, current_batch } from './batch.js'; +import { batch_deriveds } from './batch.js'; import { unset_context } from './async.js'; /** @type {Effect | null} */ @@ -131,7 +130,7 @@ export function async_derived(fn, location) { prev = promise; - var batch = (boundary.batch ??= /** @type {Batch} */ (current_batch)); + var batch = boundary.get_batch(); var pending = boundary.pending; if (should_suspend) {