diff --git a/.changeset/shaggy-donuts-wait.md b/.changeset/shaggy-donuts-wait.md new file mode 100644 index 0000000000..a155c719ed --- /dev/null +++ b/.changeset/shaggy-donuts-wait.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: avoid false-positive infinite loop error diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 123bc95d16..d80011e9b1 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -78,6 +78,12 @@ let last_scheduled_effect = null; let is_flushing = false; let is_flushing_sync = false; + +/** @type {Map} */ +let effect_execution_count = new Map(); + +let flush_count = 0; + export class Batch { /** * The current values of any sources that are updated in this batch @@ -526,7 +532,7 @@ function flush_effects() { is_flushing = true; try { - var flush_count = 0; + flush_count = 0; set_is_updating_effect(true); while (queued_root_effects.length > 0) { @@ -564,6 +570,7 @@ function flush_effects() { } finally { is_flushing = false; set_is_updating_effect(was_updating_effect); + effect_execution_count.clear(); last_scheduled_effect = null; } @@ -626,6 +633,17 @@ function flush_queued_effects(effects) { current_batch.current.size > n && (effect.f & USER_EFFECT) !== 0 ) { + var execution_count = (effect_execution_count.get(effect) ?? 0) + 1; + effect_execution_count.set(effect, execution_count); + + // If many effects are executed, they cause another flush loop, which could + // lead to false-positives in the infinite loop detection. Therefore decrease + // the counter unless the individual effect has been executed many times, which + // indeed hints at an infinite loop. + if (execution_count < 1000) { + flush_count--; + } + break; } } diff --git a/packages/svelte/tests/runtime-runes/samples/effect-loop-3/Component.svelte b/packages/svelte/tests/runtime-runes/samples/effect-loop-3/Component.svelte new file mode 100644 index 0000000000..f045d5b09c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-loop-3/Component.svelte @@ -0,0 +1,8 @@ + + +{a} diff --git a/packages/svelte/tests/runtime-runes/samples/effect-loop-3/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-loop-3/_config.js new file mode 100644 index 0000000000..61b22e358e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-loop-3/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client', 'hydrate'], + + compileOptions: { + dev: true + }, + + html: `1`.repeat(2000) +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-loop-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-loop-3/main.svelte new file mode 100644 index 0000000000..4f137cdb3a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-loop-3/main.svelte @@ -0,0 +1,7 @@ + + +{#each Array(2000) as _, i} + +{/each}