From 0c5bfad855833458d1690c511f462235313fea80 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 16:54:07 -0500 Subject: [PATCH 01/23] this appears to be unnecessary --- packages/svelte/src/internal/client/runtime.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 8c98f948f3f0..b5cee5cd9eb7 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -726,9 +726,7 @@ function flush_queued_effects(effects) { function process_deferred() { is_micro_task_queued = false; - if (flush_count > 1001) { - return; - } + const previous_queued_root_effects = queued_root_effects; queued_root_effects = []; flush_queued_root_effects(previous_queued_root_effects); From 00b46458872dde6f3e3a0015b45d8ccc40e96644 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 17:10:13 -0500 Subject: [PATCH 02/23] DRY out --- packages/svelte/src/internal/client/runtime.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index b5cee5cd9eb7..cc8c26bb8c85 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -661,6 +661,8 @@ function infinite_loop_guard() { * @returns {void} */ function flush_queued_root_effects(root_effects) { + queued_root_effects = []; + var length = root_effects.length; if (length === 0) { return; @@ -727,9 +729,7 @@ function flush_queued_effects(effects) { function process_deferred() { is_micro_task_queued = false; - const previous_queued_root_effects = queued_root_effects; - queued_root_effects = []; - flush_queued_root_effects(previous_queued_root_effects); + flush_queued_root_effects(queued_root_effects); if (!is_micro_task_queued) { flush_count = 0; @@ -857,10 +857,9 @@ export function flush_sync(fn) { infinite_loop_guard(); scheduler_mode = FLUSH_SYNC; - queued_root_effects = []; is_micro_task_queued = false; - flush_queued_root_effects(previous_queued_root_effects); + flush_queued_root_effects(queued_root_effects); var result = fn?.(); From 7d6d94c594329a70e04346af295841553196d026 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 17:11:20 -0500 Subject: [PATCH 03/23] this doesnt appear to do anything useful --- packages/svelte/src/internal/client/runtime.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index cc8c26bb8c85..86438df527a0 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -851,7 +851,6 @@ function process_effects(effect) { */ export function flush_sync(fn) { var previous_scheduler_mode = scheduler_mode; - var previous_queued_root_effects = queued_root_effects; try { infinite_loop_guard(); @@ -877,7 +876,6 @@ export function flush_sync(fn) { return result; } finally { scheduler_mode = previous_scheduler_mode; - queued_root_effects = previous_queued_root_effects; } } From 02a7cf887f341eeb7a1e189b6ad4d61420150f4b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 21:29:31 -0500 Subject: [PATCH 04/23] simplify --- .../svelte/src/internal/client/runtime.js | 57 +++++++++---------- .../samples/animation-css/_config.js | 2 + .../samples/animation-js-easing/_config.js | 2 + .../samples/animation-js/_config.js | 5 ++ .../dynamic-element-animation/_config.js | 2 + 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 86438df527a0..49bd9253dadd 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -9,7 +9,6 @@ import { } from './reactivity/effects.js'; import { EFFECT, - RENDER_EFFECT, DIRTY, MAYBE_DIRTY, CLEAN, @@ -41,15 +40,11 @@ import { } from './context.js'; import { is_firefox } from './dom/operations.js'; -const FLUSH_MICROTASK = 0; -const FLUSH_SYNC = 1; // Used for DEV time error handling /** @param {WeakSet} value */ const handled_errors = new WeakSet(); export let is_throwing_error = false; -// Used for controlling the flush of effects. -let scheduler_mode = FLUSH_MICROTASK; // Used for handling scheduling let is_micro_task_queued = false; @@ -672,6 +667,8 @@ function flush_queued_root_effects(root_effects) { var previously_flushing_effect = is_flushing_effect; is_flushing_effect = true; + is_flushing = true; + try { for (var i = 0; i < length; i++) { var effect = root_effects[i]; @@ -683,8 +680,13 @@ function flush_queued_root_effects(root_effects) { var collected_effects = process_effects(effect); flush_queued_effects(collected_effects); } + + if (queued_root_effects.length > 0) { + flush_queued_root_effects(queued_root_effects); + } } finally { is_flushing_effect = previously_flushing_effect; + is_flushing = false; } } @@ -740,16 +742,16 @@ function process_deferred() { } } +let is_flushing = false; + /** * @param {Effect} signal * @returns {void} */ export function schedule_effect(signal) { - if (scheduler_mode === FLUSH_MICROTASK) { - if (!is_micro_task_queued) { - is_micro_task_queued = true; - queueMicrotask(process_deferred); - } + if (!is_micro_task_queued && !is_flushing) { + is_micro_task_queued = true; + queueMicrotask(process_deferred); } last_scheduled_effect = signal; @@ -850,33 +852,26 @@ function process_effects(effect) { * @returns {any} */ export function flush_sync(fn) { - var previous_scheduler_mode = scheduler_mode; - - try { - infinite_loop_guard(); + infinite_loop_guard(); - scheduler_mode = FLUSH_SYNC; - is_micro_task_queued = false; + is_micro_task_queued = false; - flush_queued_root_effects(queued_root_effects); + flush_queued_root_effects(queued_root_effects); - var result = fn?.(); + var result = fn?.(); - flush_tasks(); - if (queued_root_effects.length > 0) { - flush_sync(); - } - - flush_count = 0; - last_scheduled_effect = null; - if (DEV) { - dev_effect_stack = []; - } + flush_tasks(); + if (queued_root_effects.length > 0) { + flush_sync(); + } - return result; - } finally { - scheduler_mode = previous_scheduler_mode; + flush_count = 0; + last_scheduled_effect = null; + if (DEV) { + dev_effect_stack = []; } + + return result; } /** diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js index b6b601a96b1d..b6bd818e65db 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js @@ -47,6 +47,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = target.querySelectorAll('div'); assert.ok(divs[0].getAnimations().length > 0); assert.equal(divs[1].getAnimations().length, 0); diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js index 5b7ed1c73209..f4a3554b29f9 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js @@ -46,6 +46,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); assert.equal(divs[4].dy, -120); diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js index 3606f7d17b77..1b3390e55f97 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js @@ -1,4 +1,5 @@ // @ts-nocheck +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -46,6 +47,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); assert.equal(divs[4].dy, -120); @@ -66,6 +69,8 @@ export default test({ { id: 5, name: 'e' } ]; + raf.tick(100); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js index 3d127f1375e7..05c2dc73048a 100644 --- a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js @@ -50,6 +50,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = target.querySelectorAll('div'); assert.equal(divs[0].style.transform, 'translate(0px, 120px)'); assert.equal(divs[1].style.transform, ''); From 75f0adf3ae64e5ef2f83bb17eb5a7997a13e245c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 21:35:47 -0500 Subject: [PATCH 05/23] remove unused if block --- packages/svelte/src/internal/client/runtime.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 49bd9253dadd..37cbb650d60d 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -51,7 +51,7 @@ let is_micro_task_queued = false; /** @type {Effect | null} */ let last_scheduled_effect = null; -export let is_flushing_effect = false; +export let is_flushing_effect = false; // TODO do we still need this? export let is_destroying_effect = false; /** @param {boolean} value */ @@ -733,12 +733,11 @@ function process_deferred() { flush_queued_root_effects(queued_root_effects); - if (!is_micro_task_queued) { - flush_count = 0; - last_scheduled_effect = null; - if (DEV) { - dev_effect_stack = []; - } + flush_count = 0; + last_scheduled_effect = null; + + if (DEV) { + dev_effect_stack = []; } } From a2e3fb90e2c9df19166df6851fbee5863c512a4a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 21:40:28 -0500 Subject: [PATCH 06/23] simplify, make non-recursive --- .../svelte/src/internal/client/runtime.js | 89 +++++++++---------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 37cbb650d60d..4fab73b587b4 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -615,40 +615,36 @@ function log_effect_stack() { } function infinite_loop_guard() { - if (flush_count > 1000) { - flush_count = 0; - try { - e.effect_update_depth_exceeded(); - } catch (error) { + try { + e.effect_update_depth_exceeded(); + } catch (error) { + if (DEV) { + // stack is garbage, ignore. Instead add a console.error message. + define_property(error, 'stack', { + value: '' + }); + } + // Try and handle the error so it can be caught at a boundary, that's + // if there's an effect available from when it was last scheduled + if (last_scheduled_effect !== null) { if (DEV) { - // stack is garbage, ignore. Instead add a console.error message. - define_property(error, 'stack', { - value: '' - }); - } - // Try and handle the error so it can be caught at a boundary, that's - // if there's an effect available from when it was last scheduled - if (last_scheduled_effect !== null) { - if (DEV) { - try { - handle_error(error, last_scheduled_effect, null, null); - } catch (e) { - // Only log the effect stack if the error is re-thrown - log_effect_stack(); - throw e; - } - } else { + try { handle_error(error, last_scheduled_effect, null, null); - } - } else { - if (DEV) { + } catch (e) { + // Only log the effect stack if the error is re-thrown log_effect_stack(); + throw e; } - throw error; + } else { + handle_error(error, last_scheduled_effect, null, null); + } + } else { + if (DEV) { + log_effect_stack(); } + throw error; } } - flush_count++; } /** @@ -656,33 +652,34 @@ function infinite_loop_guard() { * @returns {void} */ function flush_queued_root_effects(root_effects) { - queued_root_effects = []; - - var length = root_effects.length; - if (length === 0) { - return; - } - infinite_loop_guard(); - var previously_flushing_effect = is_flushing_effect; is_flushing_effect = true; - is_flushing = true; try { - for (var i = 0; i < length; i++) { - var effect = root_effects[i]; + var length = queued_root_effects.length; + var flush_count = 0; - if ((effect.f & CLEAN) === 0) { - effect.f ^= CLEAN; + while (queued_root_effects.length > 0) { + if (flush_count++ > 1000) { + infinite_loop_guard(); } - var collected_effects = process_effects(effect); - flush_queued_effects(collected_effects); - } + var root_effects = queued_root_effects; + var length = root_effects.length; + + queued_root_effects = []; - if (queued_root_effects.length > 0) { - flush_queued_root_effects(queued_root_effects); + for (var i = 0; i < length; i++) { + var effect = root_effects[i]; + + if ((effect.f & CLEAN) === 0) { + effect.f ^= CLEAN; + } + + var collected_effects = process_effects(effect); + flush_queued_effects(collected_effects); + } } } finally { is_flushing_effect = previously_flushing_effect; @@ -851,8 +848,6 @@ function process_effects(effect) { * @returns {any} */ export function flush_sync(fn) { - infinite_loop_guard(); - is_micro_task_queued = false; flush_queued_root_effects(queued_root_effects); From 3db7b311f49604d6b9e289f72db24454f5ac99ee Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 21:41:39 -0500 Subject: [PATCH 07/23] unused --- packages/svelte/src/internal/client/runtime.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 4fab73b587b4..3e96ea403994 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -69,7 +69,6 @@ export function set_is_destroying_effect(value) { /** @type {Effect[]} */ let queued_root_effects = []; -let flush_count = 0; /** @type {Effect[]} Stack of effects, dev only */ let dev_effect_stack = []; // Handle signal reactivity tree dependencies and reactions @@ -730,7 +729,6 @@ function process_deferred() { flush_queued_root_effects(queued_root_effects); - flush_count = 0; last_scheduled_effect = null; if (DEV) { @@ -859,7 +857,6 @@ export function flush_sync(fn) { flush_sync(); } - flush_count = 0; last_scheduled_effect = null; if (DEV) { dev_effect_stack = []; From 8f66f9df11e85cf26290c6b7a172b4d570f26fa9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 21:44:12 -0500 Subject: [PATCH 08/23] DRY --- packages/svelte/src/internal/client/runtime.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 3e96ea403994..c8259c929e1b 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -652,6 +652,8 @@ function infinite_loop_guard() { */ function flush_queued_root_effects(root_effects) { var previously_flushing_effect = is_flushing_effect; + + is_micro_task_queued = false; is_flushing_effect = true; is_flushing = true; @@ -725,8 +727,6 @@ function flush_queued_effects(effects) { } function process_deferred() { - is_micro_task_queued = false; - flush_queued_root_effects(queued_root_effects); last_scheduled_effect = null; @@ -846,8 +846,6 @@ function process_effects(effect) { * @returns {any} */ export function flush_sync(fn) { - is_micro_task_queued = false; - flush_queued_root_effects(queued_root_effects); var result = fn?.(); From a45322659721c8ea780c2cd433ccc688a32f4285 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 21:46:35 -0500 Subject: [PATCH 09/23] simplify --- .../svelte/src/internal/client/runtime.js | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index c8259c929e1b..f490de45b1c3 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -646,11 +646,7 @@ function infinite_loop_guard() { } } -/** - * @param {Array} root_effects - * @returns {void} - */ -function flush_queued_root_effects(root_effects) { +function flush_queued_root_effects() { var previously_flushing_effect = is_flushing_effect; is_micro_task_queued = false; @@ -685,6 +681,11 @@ function flush_queued_root_effects(root_effects) { } finally { is_flushing_effect = previously_flushing_effect; is_flushing = false; + + last_scheduled_effect = null; + if (DEV) { + dev_effect_stack = []; + } } } @@ -726,16 +727,6 @@ function flush_queued_effects(effects) { } } -function process_deferred() { - flush_queued_root_effects(queued_root_effects); - - last_scheduled_effect = null; - - if (DEV) { - dev_effect_stack = []; - } -} - let is_flushing = false; /** @@ -745,7 +736,7 @@ let is_flushing = false; export function schedule_effect(signal) { if (!is_micro_task_queued && !is_flushing) { is_micro_task_queued = true; - queueMicrotask(process_deferred); + queueMicrotask(flush_queued_root_effects); } last_scheduled_effect = signal; @@ -846,7 +837,7 @@ function process_effects(effect) { * @returns {any} */ export function flush_sync(fn) { - flush_queued_root_effects(queued_root_effects); + flush_queued_root_effects(); var result = fn?.(); @@ -855,11 +846,6 @@ export function flush_sync(fn) { flush_sync(); } - last_scheduled_effect = null; - if (DEV) { - dev_effect_stack = []; - } - return result; } From e42172add4c608d637e65e050ceb0a321f458813 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:01:03 -0500 Subject: [PATCH 10/23] tidy up --- .../svelte/src/internal/client/dom/task.js | 47 +++++++++---------- .../svelte/src/internal/client/runtime.js | 12 ++--- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/task.js b/packages/svelte/src/internal/client/dom/task.js index acb5a5b117f0..fe37d4828f7a 100644 --- a/packages/svelte/src/internal/client/dom/task.js +++ b/packages/svelte/src/internal/client/dom/task.js @@ -6,25 +6,21 @@ export const request_idle_callback = ? (/** @type {() => void} */ cb) => setTimeout(cb, 1) : requestIdleCallback; -let is_micro_task_queued = false; -let is_idle_task_queued = false; - /** @type {Array<() => void>} */ -let current_queued_micro_tasks = []; +let micro_tasks = []; + /** @type {Array<() => void>} */ -let current_queued_idle_tasks = []; +let idle_tasks = []; -function process_micro_tasks() { - is_micro_task_queued = false; - const tasks = current_queued_micro_tasks.slice(); - current_queued_micro_tasks = []; +function run_micro_tasks() { + var tasks = micro_tasks; + micro_tasks = []; run_all(tasks); } -function process_idle_tasks() { - is_idle_task_queued = false; - const tasks = current_queued_idle_tasks.slice(); - current_queued_idle_tasks = []; +function run_idle_tasks() { + var tasks = idle_tasks; + idle_tasks = []; run_all(tasks); } @@ -32,32 +28,33 @@ function process_idle_tasks() { * @param {() => void} fn */ export function queue_micro_task(fn) { - if (!is_micro_task_queued) { - is_micro_task_queued = true; - queueMicrotask(process_micro_tasks); + if (micro_tasks.length === 0) { + queueMicrotask(run_micro_tasks); } - current_queued_micro_tasks.push(fn); + + micro_tasks.push(fn); } /** * @param {() => void} fn */ export function queue_idle_task(fn) { - if (!is_idle_task_queued) { - is_idle_task_queued = true; - request_idle_callback(process_idle_tasks); + if (idle_tasks.length === 0) { + request_idle_callback(run_idle_tasks); } - current_queued_idle_tasks.push(fn); + + idle_tasks.push(fn); } /** * Synchronously run any queued tasks. */ export function flush_tasks() { - if (is_micro_task_queued) { - process_micro_tasks(); + if (micro_tasks.length > 0) { + run_micro_tasks(); } - if (is_idle_task_queued) { - process_idle_tasks(); + + if (idle_tasks.length > 0) { + run_idle_tasks(); } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index f490de45b1c3..647512b55fb4 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -668,13 +668,13 @@ function flush_queued_root_effects() { queued_root_effects = []; for (var i = 0; i < length; i++) { - var effect = root_effects[i]; + var root = root_effects[i]; - if ((effect.f & CLEAN) === 0) { - effect.f ^= CLEAN; + if ((root.f & CLEAN) === 0) { + root.f ^= CLEAN; } - var collected_effects = process_effects(effect); + var collected_effects = process_effects(root); flush_queued_effects(collected_effects); } } @@ -739,9 +739,7 @@ export function schedule_effect(signal) { queueMicrotask(flush_queued_root_effects); } - last_scheduled_effect = signal; - - var effect = signal; + var effect = (last_scheduled_effect = signal); while (effect.parent !== null) { effect = effect.parent; From 36092a57569a9124edcced9c7fc9d76fc6da04ab Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:07:02 -0500 Subject: [PATCH 11/23] simplify --- packages/svelte/src/internal/client/runtime.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 647512b55fb4..d2b1b8d7d963 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -45,8 +45,7 @@ import { is_firefox } from './dom/operations.js'; const handled_errors = new WeakSet(); export let is_throwing_error = false; -// Used for handling scheduling -let is_micro_task_queued = false; +let is_flushing = false; /** @type {Effect | null} */ let last_scheduled_effect = null; @@ -648,10 +647,7 @@ function infinite_loop_guard() { function flush_queued_root_effects() { var previously_flushing_effect = is_flushing_effect; - - is_micro_task_queued = false; is_flushing_effect = true; - is_flushing = true; try { var length = queued_root_effects.length; @@ -727,15 +723,13 @@ function flush_queued_effects(effects) { } } -let is_flushing = false; - /** * @param {Effect} signal * @returns {void} */ export function schedule_effect(signal) { - if (!is_micro_task_queued && !is_flushing) { - is_micro_task_queued = true; + if (!is_flushing) { + is_flushing = true; queueMicrotask(flush_queued_root_effects); } From 946e00dcf7f773a37ffd8a5746aa7ac36ff9fc03 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:07:59 -0500 Subject: [PATCH 12/23] changeset --- .changeset/mighty-dryers-smoke.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/mighty-dryers-smoke.md diff --git a/.changeset/mighty-dryers-smoke.md b/.changeset/mighty-dryers-smoke.md new file mode 100644 index 000000000000..c9b638aacdb2 --- /dev/null +++ b/.changeset/mighty-dryers-smoke.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly initialise effects inside nested root From a36e83f4e762375aee499b36bc31aa07224b5582 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:15:14 -0500 Subject: [PATCH 13/23] unused --- .../svelte/tests/runtime-legacy/samples/animation-js/_config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js index 1b3390e55f97..a2e17b49f869 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js @@ -1,5 +1,4 @@ // @ts-nocheck -import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ From 99033bdbfa6f35d5dd4baf24bad97e59f72f1e48 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:16:06 -0500 Subject: [PATCH 14/23] Revert "changeset" This reverts commit 946e00dcf7f773a37ffd8a5746aa7ac36ff9fc03. --- .changeset/mighty-dryers-smoke.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/mighty-dryers-smoke.md diff --git a/.changeset/mighty-dryers-smoke.md b/.changeset/mighty-dryers-smoke.md deleted file mode 100644 index c9b638aacdb2..000000000000 --- a/.changeset/mighty-dryers-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly initialise effects inside nested root From c45b60c93575f8cac4df5842714a6d7341aa8b2c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:24:37 -0500 Subject: [PATCH 15/23] make flush_sync non-recursive --- packages/svelte/src/internal/client/runtime.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d2b1b8d7d963..1b2cc4a6cdb4 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -834,8 +834,10 @@ export function flush_sync(fn) { var result = fn?.(); flush_tasks(); - if (queued_root_effects.length > 0) { - flush_sync(); + + while (queued_root_effects.length > 0) { + flush_queued_root_effects(); + flush_tasks(); } return result; From 8af20fe3fc641aeb8d0a0705c5d905183c02f7e9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:38:04 -0500 Subject: [PATCH 16/23] fix flushSync types --- .changeset/violet-camels-heal.md | 5 +++ .../3-transform/client/transform-client.js | 2 +- packages/svelte/src/index-client.js | 12 +---- .../src/internal/client/dom/blocks/await.js | 4 +- packages/svelte/src/internal/client/index.js | 2 +- .../svelte/src/internal/client/runtime.js | 17 +++---- packages/svelte/src/legacy/legacy-client.js | 6 +-- packages/svelte/tests/store/test.ts | 8 ++-- packages/svelte/types/index.d.ts | 45 ++++++++++--------- 9 files changed, 50 insertions(+), 51 deletions(-) create mode 100644 .changeset/violet-camels-heal.md diff --git a/.changeset/violet-camels-heal.md b/.changeset/violet-camels-heal.md new file mode 100644 index 000000000000..31e72fa33d53 --- /dev/null +++ b/.changeset/violet-camels-heal.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update types and inline docs for flushSync diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 2e6307a4b7a6..cf5ba285cbf3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -312,7 +312,7 @@ export function client_component(analysis, options) { const setter = b.set(key, [ b.stmt(b.call(b.id(name), b.id('$$value'))), - b.stmt(b.call('$.flush_sync')) + b.stmt(b.call('$.flush')) ]); if (analysis.runes && binding.initial) { diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index ca29d5bfbe3c..efcf7b727b8d 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -1,7 +1,7 @@ /** @import { ComponentContext, ComponentContextLegacy } from '#client' */ /** @import { EventDispatcher } from './index.js' */ /** @import { NotFunction } from './internal/types.js' */ -import { flush_sync, untrack } from './internal/client/runtime.js'; +import { untrack } from './internal/client/runtime.js'; import { is_array } from './internal/shared/utils.js'; import { user_effect } from './internal/client/index.js'; import * as e from './internal/client/errors.js'; @@ -206,15 +206,7 @@ function init_update_callbacks(context) { return (l.u ??= { a: [], b: [], m: [] }); } -/** - * Synchronously flushes any pending state changes and those that result from it. - * @param {() => void} [fn] - * @returns {void} - */ -export function flushSync(fn) { - flush_sync(fn); -} - +export { flushSync } from './internal/client/runtime.js'; export { getContext, getAllContexts, hasContext, setContext } from './internal/client/context.js'; export { hydrate, mount, unmount } from './internal/client/render.js'; export { tick, untrack } from './internal/client/runtime.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index c8c7c1c0ea77..2e3d22977914 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -3,7 +3,7 @@ import { DEV } from 'esm-env'; import { is_promise } from '../../../shared/utils.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { internal_set, mutable_source, source } from '../../reactivity/sources.js'; -import { flush_sync, set_active_effect, set_active_reaction } from '../../runtime.js'; +import { flushSync, set_active_effect, set_active_reaction } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; import { queue_micro_task } from '../task.js'; import { UNINITIALIZED } from '../../../../constants.js'; @@ -105,7 +105,7 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { // without this, the DOM does not update until two ticks after the promise // resolves, which is unexpected behaviour (and somewhat irksome to test) - flush_sync(); + flushSync(); } } } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index d78f6d452e84..f5ccb9413ba0 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -137,7 +137,7 @@ export { get, safe_get, invalidate_inner_signals, - flush_sync, + flushSync as flush, tick, untrack, exclude_from_object, diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 1b2cc4a6cdb4..cba30376ab08 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -823,12 +823,13 @@ function process_effects(effect) { } /** - * Internal version of `flushSync` with the option to not flush previous effects. - * Returns the result of the passed function, if given. - * @param {() => any} [fn] - * @returns {any} + * Synchronously flush any pending updates. + * Returns void if no callback is provided, otherwise returns the result of calling the callback. + * @template [T=void] + * @param {(() => T) | undefined} [fn] + * @returns {T extends void ? void : T} */ -export function flush_sync(fn) { +export function flushSync(fn) { flush_queued_root_effects(); var result = fn?.(); @@ -840,7 +841,7 @@ export function flush_sync(fn) { flush_tasks(); } - return result; + return /** @type {T extends void ? void : T} */ (result); } /** @@ -849,9 +850,9 @@ export function flush_sync(fn) { */ export async function tick() { await Promise.resolve(); - // By calling flush_sync we guarantee that any pending state changes are applied after one tick. + // By calling flushSync we guarantee that any pending state changes are applied after one tick. // TODO look into whether we can make flushing subsequent updates synchronously in the future. - flush_sync(); + flushSync(); } /** diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index 3a05bc04963f..bb9a5a9c039b 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -3,7 +3,7 @@ import { DIRTY, LEGACY_PROPS, MAYBE_DIRTY } from '../internal/client/constants.j import { user_pre_effect } from '../internal/client/reactivity/effects.js'; import { mutable_source, set } from '../internal/client/reactivity/sources.js'; import { hydrate, mount, unmount } from '../internal/client/render.js'; -import { active_effect, flush_sync, get, set_signal_status } from '../internal/client/runtime.js'; +import { active_effect, flushSync, get, set_signal_status } from '../internal/client/runtime.js'; import { lifecycle_outside_component } from '../internal/shared/errors.js'; import { define_property, is_array } from '../internal/shared/utils.js'; import * as w from '../internal/client/warnings.js'; @@ -119,9 +119,9 @@ class Svelte4Component { recover: options.recover }); - // We don't flush_sync for custom element wrappers or if the user doesn't want it + // We don't flushSync for custom element wrappers or if the user doesn't want it if (!options?.props?.$$host || options.sync === false) { - flush_sync(); + flushSync(); } this.#events = props.$$events; diff --git a/packages/svelte/tests/store/test.ts b/packages/svelte/tests/store/test.ts index b23ea195d6c9..77cecca7e525 100644 --- a/packages/svelte/tests/store/test.ts +++ b/packages/svelte/tests/store/test.ts @@ -602,7 +602,7 @@ describe('toStore', () => { assert.deepEqual(log, [0]); set(count, 1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); unsubscribe(); @@ -625,7 +625,7 @@ describe('toStore', () => { assert.deepEqual(log, [0]); set(count, 1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); store.set(2); @@ -654,11 +654,11 @@ describe('fromStore', () => { assert.deepEqual(log, [0]); store.set(1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); count.current = 2; - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1, 2]); assert.equal(get(store), 2); diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 77d78477ee93..4bfb160395f1 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -408,10 +408,6 @@ declare module 'svelte' { * @deprecated Use [`$effect`](https://svelte.dev/docs/svelte/$effect) instead * */ export function afterUpdate(fn: () => void): void; - /** - * Synchronously flushes any pending state changes and those that result from it. - * */ - export function flushSync(fn?: (() => void) | undefined): void; /** * Create a snippet programmatically * */ @@ -421,6 +417,29 @@ declare module 'svelte' { }): Snippet; /** Anything except a function */ type NotFunction = T extends Function ? never : T; + /** + * Synchronously flush any pending updates. + * Returns void if no callback is provided, otherwise returns the result of calling the callback. + * */ + export function flushSync(fn?: (() => T) | undefined): T extends void ? void : T; + /** + * Returns a promise that resolves once any pending state changes have been applied. + * */ + export function tick(): Promise; + /** + * When used inside a [`$derived`](https://svelte.dev/docs/svelte/$derived) or [`$effect`](https://svelte.dev/docs/svelte/$effect), + * any state read inside `fn` will not be treated as a dependency. + * + * ```ts + * $effect(() => { + * // this will run when `data` changes, but not when `time` changes + * save(data, { + * timestamp: untrack(() => time) + * }); + * }); + * ``` + * */ + export function untrack(fn: () => T): T; /** * Retrieves the context that belongs to the closest parent component with the specified `key`. * Must be called during component initialisation. @@ -494,24 +513,6 @@ declare module 'svelte' { export function unmount(component: Record, options?: { outro?: boolean; } | undefined): Promise; - /** - * Returns a promise that resolves once any pending state changes have been applied. - * */ - export function tick(): Promise; - /** - * When used inside a [`$derived`](https://svelte.dev/docs/svelte/$derived) or [`$effect`](https://svelte.dev/docs/svelte/$effect), - * any state read inside `fn` will not be treated as a dependency. - * - * ```ts - * $effect(() => { - * // this will run when `data` changes, but not when `time` changes - * save(data, { - * timestamp: untrack(() => time) - * }); - * }); - * ``` - * */ - export function untrack(fn: () => T): T; type Getters = { [K in keyof T]: () => T[K]; }; From ccfcbfc9b30d376a3d19ec2e53501c48f1672d7c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:47:55 -0500 Subject: [PATCH 17/23] fix --- benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_broad.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_deep.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_mux.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js | 4 ++-- benchmarking/benchmarks/reactivity/mol_bench.js | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js index 6b058cdc3c1d..9daea6de99cb 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js @@ -20,12 +20,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(computed5) === 6); for (let i = 0; i < 1000; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(computed5) === 6); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js index d1cde5958edf..8dc5710c87db 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js @@ -25,12 +25,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); counter = 0; for (let i = 0; i < 50; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(last) === i + 50); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js index 149457ede156..8690c85f864a 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js @@ -25,12 +25,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); counter = 0; for (let i = 0; i < iter; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(current) === len + i); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js index 958a1bcd7890..bf4e07ee8962 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js @@ -28,13 +28,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(sum) === 2 * width); counter = 0; for (let i = 0; i < 500; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(sum) === (i + 1) * width); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js index b645051c09bf..fc252a27b5f8 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js @@ -22,13 +22,13 @@ function setup() { destroy, run() { for (let i = 0; i < 10; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(heads[i], i); }); assert($.get(splited[i]) === i + 1); } for (let i = 0; i < 10; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(heads[i], i * 2); }); assert($.get(splited[i]) === i * 2 + 1); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js index 53b85acd3766..3bee06ca0e8f 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js @@ -25,13 +25,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(current) === size); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(current) === i * size); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js index b9e2ad9fa4a3..11a419a52e7b 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js @@ -38,13 +38,13 @@ function setup() { destroy, run() { const constant = count(width); - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(sum) === constant); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(sum) === constant - width + i * width); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js index 0e783732dc67..54eb732cb29d 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js @@ -25,13 +25,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(current) === 40); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); } diff --git a/benchmarking/benchmarks/reactivity/mol_bench.js b/benchmarking/benchmarks/reactivity/mol_bench.js index c9f492f61967..536b078d74a4 100644 --- a/benchmarking/benchmarks/reactivity/mol_bench.js +++ b/benchmarking/benchmarks/reactivity/mol_bench.js @@ -51,11 +51,11 @@ function setup() { */ run(i) { res.length = 0; - $.flush_sync(() => { + $.flush(() => { $.set(B, 1); $.set(A, 1 + i * 2); }); - $.flush_sync(() => { + $.flush(() => { $.set(A, 2 + i * 2); $.set(B, 2); }); From c55a698e29270acc3733526be1602b39ceca3c62 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 22:53:51 -0500 Subject: [PATCH 18/23] unused --- packages/svelte/src/internal/client/runtime.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index cba30376ab08..9d4448d845ca 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -650,7 +650,6 @@ function flush_queued_root_effects() { is_flushing_effect = true; try { - var length = queued_root_effects.length; var flush_count = 0; while (queued_root_effects.length > 0) { From 763ed356882ed4f4a6866cef10e297166a331c96 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 23:00:33 -0500 Subject: [PATCH 19/23] simplify --- .../src/internal/client/reactivity/effects.js | 8 ------ .../src/internal/client/reactivity/sources.js | 26 +++++++------------ .../svelte/src/internal/client/runtime.js | 12 +++------ 3 files changed, 13 insertions(+), 33 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 9d7b5e9de624..28589ce94df1 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -6,15 +6,12 @@ import { update_effect, get, is_destroying_effect, - is_flushing_effect, remove_reactions, schedule_effect, set_active_reaction, set_is_destroying_effect, - set_is_flushing_effect, set_signal_status, untrack, - skip_reaction, untracking } from '../runtime.js'; import { @@ -118,17 +115,12 @@ function create_effect(type, fn, sync, push = true) { } if (sync) { - var previously_flushing_effect = is_flushing_effect; - try { - set_is_flushing_effect(true); update_effect(effect); effect.f |= EFFECT_RAN; } catch (e) { destroy_effect(effect); throw e; - } finally { - set_is_flushing_effect(previously_flushing_effect); } } else if (fn !== null) { schedule_effect(effect); diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index ded0ca05846b..f6a3fd7e330a 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -14,8 +14,6 @@ import { derived_sources, set_derived_sources, check_dirtiness, - set_is_flushing_effect, - is_flushing_effect, untracking } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; @@ -202,22 +200,18 @@ export function internal_set(source, value) { if (DEV && inspect_effects.size > 0) { const inspects = Array.from(inspect_effects); - var previously_flushing_effect = is_flushing_effect; - set_is_flushing_effect(true); - try { - for (const effect of inspects) { - // Mark clean inspect-effects as maybe dirty and then check their dirtiness - // instead of just updating the effects - this way we avoid overfiring. - if ((effect.f & CLEAN) !== 0) { - set_signal_status(effect, MAYBE_DIRTY); - } - if (check_dirtiness(effect)) { - update_effect(effect); - } + + for (const effect of inspects) { + // Mark clean inspect-effects as maybe dirty and then check their dirtiness + // instead of just updating the effects - this way we avoid overfiring. + if ((effect.f & CLEAN) !== 0) { + set_signal_status(effect, MAYBE_DIRTY); + } + if (check_dirtiness(effect)) { + update_effect(effect); } - } finally { - set_is_flushing_effect(previously_flushing_effect); } + inspect_effects.clear(); } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 9d4448d845ca..589e6bb9dc31 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -53,11 +53,6 @@ let last_scheduled_effect = null; export let is_flushing_effect = false; // TODO do we still need this? export let is_destroying_effect = false; -/** @param {boolean} value */ -export function set_is_flushing_effect(value) { - is_flushing_effect = value; -} - /** @param {boolean} value */ export function set_is_destroying_effect(value) { is_destroying_effect = value; @@ -552,8 +547,10 @@ export function update_effect(effect) { var previous_effect = active_effect; var previous_component_context = component_context; + var previously_flushing_effect = is_flushing_effect; active_effect = effect; + is_flushing_effect = true; if (DEV) { var previous_component_fn = dev_current_component_function; @@ -595,6 +592,7 @@ export function update_effect(effect) { } catch (error) { handle_error(error, effect, previous_effect, previous_component_context || effect.ctx); } finally { + is_flushing_effect = previously_flushing_effect; active_effect = previous_effect; if (DEV) { @@ -646,9 +644,6 @@ function infinite_loop_guard() { } function flush_queued_root_effects() { - var previously_flushing_effect = is_flushing_effect; - is_flushing_effect = true; - try { var flush_count = 0; @@ -674,7 +669,6 @@ function flush_queued_root_effects() { } } } finally { - is_flushing_effect = previously_flushing_effect; is_flushing = false; last_scheduled_effect = null; From 9da49279495c43be0e0539d78f089898453db2d9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 23:01:56 -0500 Subject: [PATCH 20/23] tidy up --- packages/svelte/src/internal/client/runtime.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 589e6bb9dc31..2f66d594c90f 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -50,7 +50,8 @@ let is_flushing = false; /** @type {Effect | null} */ let last_scheduled_effect = null; -export let is_flushing_effect = false; // TODO do we still need this? +let is_updating_effect = false; + export let is_destroying_effect = false; /** @param {boolean} value */ @@ -401,7 +402,7 @@ export function update_reaction(reaction) { active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; skip_reaction = (flags & UNOWNED) !== 0 && - (!is_flushing_effect || previous_reaction === null || previous_untracking); + (!is_updating_effect || previous_reaction === null || previous_untracking); derived_sources = null; set_component_context(reaction.ctx); @@ -547,10 +548,10 @@ export function update_effect(effect) { var previous_effect = active_effect; var previous_component_context = component_context; - var previously_flushing_effect = is_flushing_effect; + var was_updating_effect = is_updating_effect; active_effect = effect; - is_flushing_effect = true; + is_updating_effect = true; if (DEV) { var previous_component_fn = dev_current_component_function; @@ -592,7 +593,7 @@ export function update_effect(effect) { } catch (error) { handle_error(error, effect, previous_effect, previous_component_context || effect.ctx); } finally { - is_flushing_effect = previously_flushing_effect; + is_updating_effect = was_updating_effect; active_effect = previous_effect; if (DEV) { From 026a1f2b00a7dd4a158d32fd80955ebf9674217a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Feb 2025 23:08:41 -0500 Subject: [PATCH 21/23] tidy up --- packages/svelte/src/internal/client/runtime.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 2f66d594c90f..293d8c706d45 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -399,10 +399,9 @@ export function update_reaction(reaction) { new_deps = /** @type {null | Value[]} */ (null); skipped_deps = 0; untracked_writes = null; - active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; skip_reaction = - (flags & UNOWNED) !== 0 && - (!is_updating_effect || previous_reaction === null || previous_untracking); + (flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null); + active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; derived_sources = null; set_component_context(reaction.ctx); From a3aa4f3969c48e8328c0b52d0b5611e6678373f2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 21 Feb 2025 08:00:57 -0500 Subject: [PATCH 22/23] present unnecessary microtasks, avoid flushing if no function provided --- packages/svelte/src/internal/client/runtime.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 293d8c706d45..b5cd4b1536a3 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -823,13 +823,18 @@ function process_effects(effect) { * @returns {T extends void ? void : T} */ export function flushSync(fn) { - flush_queued_root_effects(); + var result; - var result = fn?.(); + if (fn) { + is_flushing = true; + flush_queued_root_effects(); + result = fn(); + } flush_tasks(); while (queued_root_effects.length > 0) { + is_flushing = true; flush_queued_root_effects(); flush_tasks(); } From 0b18d816be996e1e06c56d60ebcb1fecb7112037 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 21 Feb 2025 08:58:29 -0500 Subject: [PATCH 23/23] simplify --- packages/svelte/src/internal/client/runtime.js | 4 ++-- packages/svelte/types/index.d.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index b5cd4b1536a3..cd3f794421b7 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -820,7 +820,7 @@ function process_effects(effect) { * Returns void if no callback is provided, otherwise returns the result of calling the callback. * @template [T=void] * @param {(() => T) | undefined} [fn] - * @returns {T extends void ? void : T} + * @returns {T} */ export function flushSync(fn) { var result; @@ -839,7 +839,7 @@ export function flushSync(fn) { flush_tasks(); } - return /** @type {T extends void ? void : T} */ (result); + return /** @type {T} */ (result); } /** diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 4bfb160395f1..4cb66aa87d30 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -421,7 +421,7 @@ declare module 'svelte' { * Synchronously flush any pending updates. * Returns void if no callback is provided, otherwise returns the result of calling the callback. * */ - export function flushSync(fn?: (() => T) | undefined): T extends void ? void : T; + export function flushSync(fn?: (() => T) | undefined): T; /** * Returns a promise that resolves once any pending state changes have been applied. * */