From 7aa34b4eebae3a812c24e5ae6990dd99326625a4 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 20 May 2024 22:01:01 +0100 Subject: [PATCH] breaking: delegated event handlers now yield effect updates --- .changeset/popular-cameras-tie.md | 5 ++ .../internal/client/dom/elements/events.js | 4 ++ .../svelte/src/internal/client/runtime.js | 49 ++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 .changeset/popular-cameras-tie.md diff --git a/.changeset/popular-cameras-tie.md b/.changeset/popular-cameras-tie.md new file mode 100644 index 0000000000..7c306a8c3e --- /dev/null +++ b/.changeset/popular-cameras-tie.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +breaking: delegated event handlers now yield effect updates diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index e0298af92a..b31fe7fdcc 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -1,5 +1,6 @@ import { render_effect } from '../../reactivity/effects.js'; import { all_registered_events, root_event_handles } from '../../render.js'; +import { FLUSH_YIELD, current_scheduler_mode, set_schedule_mode } from '../../runtime.js'; import { define_property, is_array } from '../../utils.js'; import { hydrating } from '../hydration.js'; @@ -180,9 +181,12 @@ export function handle_event_propagation(handler_element, event) { } } + const previous_scheduler_mode = current_scheduler_mode; try { + set_schedule_mode(FLUSH_YIELD); next(current_target); } finally { + set_schedule_mode(previous_scheduler_mode); // @ts-expect-error is used above event.__root = handler_element; // @ts-expect-error is used above diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 43ccd4d703..935bbe3734 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -28,17 +28,27 @@ import { lifecycle_outside_component } from '../shared/errors.js'; const FLUSH_MICROTASK = 0; const FLUSH_SYNC = 1; +export const FLUSH_YIELD = 2; // Used for DEV time error handling /** @param {WeakSet} value */ const handled_errors = new WeakSet(); // Used for controlling the flush of effects. -let current_scheduler_mode = FLUSH_MICROTASK; +export let current_scheduler_mode = FLUSH_MICROTASK; // Used for handling scheduling let is_micro_task_queued = false; +let is_yield_task_queued = false; + export let is_flushing_effect = false; export let is_destroying_effect = false; +/** + * @param {number} value + */ +export function set_schedule_mode(value) { + current_scheduler_mode = value; +} + /** @param {boolean} value */ export function set_is_flushing_effect(value) { is_flushing_effect = value; @@ -521,13 +531,17 @@ function infinite_loop_guard() { * @returns {void} */ function flush_queued_root_effects(root_effects) { + const length = root_effects.length; + if (length === 0) { + return; + } infinite_loop_guard(); var previously_flushing_effect = is_flushing_effect; is_flushing_effect = true; try { - for (var i = 0; i < root_effects.length; i++) { + for (var i = 0; i < length; i++) { var effect = root_effects[i]; // When working with custom elements, the root effects might not have a root @@ -565,6 +579,7 @@ function flush_queued_effects(effects) { function process_microtask() { is_micro_task_queued = false; + is_yield_task_queued = false; if (flush_count > 101) { return; } @@ -576,6 +591,31 @@ function process_microtask() { } } +const yield_task = + typeof requestAnimationFrame === 'undefined' ? setTimeout : requestAnimationFrame; + +async function process_yieldtask() { + // TODO: replace this with scheduler.yield when it becomes standard + await new Promise((fulfil) => { + yield_task(() => { + setTimeout(fulfil, 0); + }); + // In case of background task + setTimeout(fulfil, 100); + }); + is_yield_task_queued = false; + is_micro_task_queued = false; + if (flush_count > 101) { + return; + } + const previous_queued_root_effects = current_queued_root_effects; + current_queued_root_effects = []; + flush_queued_root_effects(previous_queued_root_effects); + if (!is_yield_task_queued) { + flush_count = 0; + } +} + /** * @param {import('#client').Effect} signal * @returns {void} @@ -586,6 +626,11 @@ export function schedule_effect(signal) { is_micro_task_queued = true; queueMicrotask(process_microtask); } + } else if (current_scheduler_mode === FLUSH_YIELD) { + if (!is_yield_task_queued) { + is_yield_task_queued = true; + process_yieldtask(); + } } var effect = signal;