diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index 546abd95dd..788afa1921 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -13,7 +13,7 @@ import { set_dev_current_component_function } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; -import { queue_after_micro_task } from '../task.js'; +import { queue_post_micro_task } from '../task.js'; import { UNINITIALIZED } from '../../../../constants.js'; const PENDING = 0; @@ -148,7 +148,7 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { } else { // Wait a microtask before checking if we should show the pending state as // the promise might have resolved by the next microtask. - queue_after_micro_task(() => { + queue_post_micro_task(() => { if (!resolved) update(PENDING, true); }); } diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index e2c84e5a40..1e172ef73b 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -27,10 +27,10 @@ import { set_hydrate_node } from '../hydration.js'; import { get_next_sibling } from '../operations.js'; -import { queue_before_micro_task } from '../task.js'; +import { queue_boundary_micro_task } from '../task.js'; -const SUSPEND_INCREMENT = Symbol(); -const SUSPEND_DECREMENT = Symbol(); +const ASYNC_INCREMENT = Symbol(); +const ASYNC_DECREMENT = Symbol(); /** * @param {Effect} boundary @@ -70,10 +70,10 @@ export function boundary(node, props, boundary_fn) { /** @type {Effect} */ var boundary_effect; /** @type {Effect | null} */ - var suspended_effect = null; + var async_effect = null; /** @type {DocumentFragment | null} */ - var suspended_fragment = null; - var suspend_count = 0; + var async_fragment = null; + var async_count = 0; block(() => { var boundary = /** @type {Effect} */ (active_effect); @@ -101,27 +101,27 @@ export function boundary(node, props, boundary_fn) { boundary.fn = (/** @type {unknown} */ input) => { let pending = props.pending; - if (input === SUSPEND_INCREMENT) { + if (input === ASYNC_INCREMENT) { if (!pending) { return false; } - if (suspend_count++ === 0) { - queue_before_micro_task(() => { - if (suspended_effect) { + if (async_count++ === 0) { + queue_boundary_micro_task(() => { + if (async_effect) { return; } var effect = boundary_effect; - suspended_effect = boundary_effect; + async_effect = boundary_effect; pause_effect( - suspended_effect, + async_effect, () => { /** @type {TemplateNode | null} */ var node = effect.nodes_start; var end = effect.nodes_end; - suspended_fragment = document.createDocumentFragment(); + async_fragment = document.createDocumentFragment(); while (node !== null) { /** @type {TemplateNode | null} */ @@ -129,7 +129,7 @@ export function boundary(node, props, boundary_fn) { node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node)); node.remove(); - suspended_fragment.append(node); + async_fragment.append(node); node = sibling; } }, @@ -145,22 +145,22 @@ export function boundary(node, props, boundary_fn) { return true; } - if (input === SUSPEND_DECREMENT) { + if (input === ASYNC_DECREMENT) { if (!pending) { return false; } - if (--suspend_count === 0) { - queue_before_micro_task(() => { - if (!suspended_effect) { + if (--async_count === 0) { + queue_boundary_micro_task(() => { + if (!async_effect) { return; } if (boundary_effect) { destroy_effect(boundary_effect); } - boundary_effect = suspended_effect; - suspended_effect = null; - anchor.before(/** @type {DocumentFragment} */ (suspended_fragment)); + boundary_effect = async_effect; + async_effect = null; + anchor.before(/** @type {DocumentFragment} */ (async_fragment)); resume_effect(boundary_effect); }); } @@ -199,7 +199,7 @@ export function boundary(node, props, boundary_fn) { } if (failed) { - queue_before_micro_task(() => { + queue_boundary_micro_task(() => { render_snippet(() => { failed( anchor, @@ -226,9 +226,9 @@ export function boundary(node, props, boundary_fn) { /** * @param {Effect | null} effect - * @param {typeof SUSPEND_INCREMENT | typeof SUSPEND_DECREMENT} trigger + * @param {typeof ASYNC_INCREMENT | typeof ASYNC_DECREMENT} trigger */ -function trigger_suspense(effect, trigger) { +export function trigger_async_boundary(effect, trigger) { var current = effect; while (current !== null) { @@ -241,17 +241,3 @@ function trigger_suspense(effect, trigger) { current = current.parent; } } - -export function create_suspense() { - var current = active_effect; - - const suspend = () => { - trigger_suspense(current, SUSPEND_INCREMENT); - }; - - const unsuspend = () => { - trigger_suspense(current, SUSPEND_DECREMENT); - }; - - return [suspend, unsuspend]; -} diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 970d3e37e5..dc4c133de4 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -34,7 +34,7 @@ import { import { source, mutable_source, internal_set } from '../../reactivity/sources.js'; import { array_from, is_array } from '../../../shared/utils.js'; import { INERT } from '../../constants.js'; -import { queue_after_micro_task } from '../task.js'; +import { queue_post_micro_task } from '../task.js'; import { active_effect, active_reaction, get } from '../../runtime.js'; import { DEV } from 'esm-env'; import { derived_safe_equal } from '../../reactivity/deriveds.js'; @@ -470,7 +470,7 @@ function reconcile(array, state, anchor, render_fn, flags, is_inert, get_key, ge } if (is_animated) { - queue_after_micro_task(() => { + queue_post_micro_task(() => { if (to_animate === undefined) return; for (item of to_animate) { item.a?.apply(); diff --git a/packages/svelte/src/internal/client/dom/css.js b/packages/svelte/src/internal/client/dom/css.js index 3934940204..d4340a07ee 100644 --- a/packages/svelte/src/internal/client/dom/css.js +++ b/packages/svelte/src/internal/client/dom/css.js @@ -1,5 +1,5 @@ import { DEV } from 'esm-env'; -import { queue_after_micro_task } from './task.js'; +import { queue_post_micro_task } from './task.js'; import { register_style } from '../dev/css.js'; /** @@ -7,8 +7,8 @@ import { register_style } from '../dev/css.js'; * @param {{ hash: string, code: string }} css */ export function append_styles(anchor, css) { - // Use `queue_after_micro_task` to ensure `anchor` is in the DOM, otherwise getRootNode() will yield wrong results - queue_after_micro_task(() => { + // Use `queue_post_micro_task` to ensure `anchor` is in the DOM, otherwise getRootNode() will yield wrong results + queue_post_micro_task(() => { var root = anchor.getRootNode(); var target = /** @type {ShadowRoot} */ (root).host diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/input.js b/packages/svelte/src/internal/client/dom/elements/bindings/input.js index 188b91fa0b..b8d4b07c9b 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -3,7 +3,7 @@ import { render_effect, teardown } from '../../../reactivity/effects.js'; import { listen_to_event_and_reset_event } from './shared.js'; import * as e from '../../../errors.js'; import { is } from '../../../proxy.js'; -import { queue_after_micro_task } from '../../task.js'; +import { queue_post_micro_task } from '../../task.js'; import { hydrating } from '../../hydration.js'; import { is_runes, untrack } from '../../../runtime.js'; @@ -158,14 +158,14 @@ export function bind_group(inputs, group_index, input, get, set = get) { if (!pending.has(binding_group)) { pending.add(binding_group); - queue_after_micro_task(() => { + queue_post_micro_task(() => { // necessary to maintain binding group order in all insertion scenarios binding_group.sort((a, b) => (a.compareDocumentPosition(b) === 4 ? -1 : 1)); pending.delete(binding_group); }); } - queue_after_micro_task(() => { + queue_post_micro_task(() => { if (hydration_mismatch) { var value; diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/this.js b/packages/svelte/src/internal/client/dom/elements/bindings/this.js index d3e2349d42..0ca5039e7c 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/this.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/this.js @@ -1,7 +1,7 @@ import { STATE_SYMBOL } from '../../../constants.js'; import { effect, render_effect } from '../../../reactivity/effects.js'; import { untrack } from '../../../runtime.js'; -import { queue_after_micro_task } from '../../task.js'; +import { queue_post_micro_task } from '../../task.js'; /** * @param {any} bound_value @@ -49,7 +49,7 @@ export function bind_this(element_or_component = {}, update, get_value, get_part return () => { // We cannot use effects in the teardown phase, we we use a microtask instead. - queue_after_micro_task(() => { + queue_post_micro_task(() => { if (parts && is_bound_this(get_value(...parts), element_or_component)) { update(null, ...parts); } diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 591faaec9c..4144a13fac 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -2,7 +2,7 @@ import { teardown } from '../../reactivity/effects.js'; import { define_property, is_array } from '../../../shared/utils.js'; import { hydrating } from '../hydration.js'; -import { queue_after_micro_task } from '../task.js'; +import { queue_post_micro_task } from '../task.js'; import { FILENAME } from '../../../../constants.js'; import * as w from '../../warnings.js'; import { @@ -77,7 +77,7 @@ export function create_event(event_name, dom, handler, options) { event_name.startsWith('touch') || event_name === 'wheel' ) { - queue_after_micro_task(() => { + queue_post_micro_task(() => { dom.addEventListener(event_name, target_handler, options); }); } else { diff --git a/packages/svelte/src/internal/client/dom/elements/misc.js b/packages/svelte/src/internal/client/dom/elements/misc.js index 0eefaf104c..dab8e84c32 100644 --- a/packages/svelte/src/internal/client/dom/elements/misc.js +++ b/packages/svelte/src/internal/client/dom/elements/misc.js @@ -1,6 +1,6 @@ import { hydrating } from '../hydration.js'; import { clear_text_content, get_first_child } from '../operations.js'; -import { queue_after_micro_task } from '../task.js'; +import { queue_post_micro_task } from '../task.js'; /** * @param {HTMLElement} dom @@ -12,7 +12,7 @@ export function autofocus(dom, value) { const body = document.body; dom.autofocus = true; - queue_after_micro_task(() => { + queue_post_micro_task(() => { if (document.activeElement === body) { dom.focus(); } diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index 9834cd05e6..0dd17fad9f 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -13,7 +13,7 @@ import { should_intro } from '../../render.js'; import { current_each_item } from '../blocks/each.js'; import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../constants.js'; import { BLOCK_EFFECT, EFFECT_RAN, EFFECT_TRANSPARENT } from '../../constants.js'; -import { queue_after_micro_task } from '../task.js'; +import { queue_post_micro_task } from '../task.js'; /** * @param {Element} element @@ -326,7 +326,7 @@ function animate(element, options, counterpart, t2, on_finish) { var a; var aborted = false; - queue_after_micro_task(() => { + queue_post_micro_task(() => { if (aborted) return; var o = options({ direction: is_intro ? 'in' : 'out' }); a = animate(element, o, counterpart, t2, on_finish); diff --git a/packages/svelte/src/internal/client/dom/task.js b/packages/svelte/src/internal/client/dom/task.js index 9f88086276..8b16b30ebe 100644 --- a/packages/svelte/src/internal/client/dom/task.js +++ b/packages/svelte/src/internal/client/dom/task.js @@ -10,59 +10,61 @@ let is_micro_task_queued = false; let is_idle_task_queued = false; /** @type {Array<() => void>} */ -let queued_before_microtasks = []; +let queued_boundary_microtasks = []; /** @type {Array<() => void>} */ -let queued_after_microtasks = []; +let queued_post_microtasks = []; /** @type {Array<() => void>} */ let queued_idle_tasks = []; -export function flush_before_micro_tasks() { - const tasks = queued_before_microtasks.slice(); - queued_before_microtasks = []; +export function flush_boundary_micro_tasks() { + const tasks = queued_boundary_microtasks.slice(); + queued_boundary_microtasks = []; run_all(tasks); } -function flush_after_micro_tasks() { - const tasks = queued_after_microtasks.slice(); - queued_after_microtasks = []; +export function flush_post_micro_tasks() { + const tasks = queued_post_microtasks.slice(); + queued_post_microtasks = []; run_all(tasks); } -function process_micro_tasks() { - if (is_micro_task_queued) { - is_micro_task_queued = false; - flush_before_micro_tasks(); - flush_after_micro_tasks(); +export function flush_idle_tasks() { + if (is_idle_task_queued) { + is_idle_task_queued = false; + const tasks = queued_idle_tasks.slice(); + queued_idle_tasks = []; + run_all(tasks); } } -function process_idle_tasks() { - is_idle_task_queued = false; - const tasks = queued_idle_tasks.slice(); - queued_idle_tasks = []; - run_all(tasks); +function flush_all_micro_tasks() { + if (is_micro_task_queued) { + is_micro_task_queued = false; + flush_boundary_micro_tasks(); + flush_post_micro_tasks(); + } } /** * @param {() => void} fn */ -export function queue_before_micro_task(fn) { +export function queue_boundary_micro_task(fn) { if (!is_micro_task_queued) { is_micro_task_queued = true; - queueMicrotask(process_micro_tasks); + queueMicrotask(flush_all_micro_tasks); } - queued_before_microtasks.push(fn); + queued_boundary_microtasks.push(fn); } /** * @param {() => void} fn */ -export function queue_after_micro_task(fn) { +export function queue_post_micro_task(fn) { if (!is_micro_task_queued) { is_micro_task_queued = true; - queueMicrotask(process_micro_tasks); + queueMicrotask(flush_all_micro_tasks); } - queued_after_microtasks.push(fn); + queued_post_microtasks.push(fn); } /** @@ -71,19 +73,7 @@ export function queue_after_micro_task(fn) { export function queue_idle_task(fn) { if (!is_idle_task_queued) { is_idle_task_queued = true; - request_idle_callback(process_idle_tasks); + request_idle_callback(flush_idle_tasks); } queued_idle_tasks.push(fn); } - -/** - * Synchronously run any queued tasks. - */ -export function flush_after_tasks() { - if (is_micro_task_queued) { - process_micro_tasks(); - } - if (is_idle_task_queued) { - process_idle_tasks(); - } -} diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 3f6a2e18e9..129260b454 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -27,7 +27,11 @@ import { DISCONNECTED, BOUNDARY_EFFECT } from './constants.js'; -import { flush_after_tasks, flush_before_micro_tasks } from './dom/task.js'; +import { + flush_idle_tasks, + flush_boundary_micro_tasks, + flush_post_micro_tasks +} from './dom/task.js'; import { add_owner } from './dev/ownership.js'; import { internal_set, set, source } from './reactivity/sources.js'; import { destroy_derived, execute_derived, update_derived } from './reactivity/deriveds.js'; @@ -812,6 +816,9 @@ function process_effects(effect, collected_effects) { current_effect.f ^= CLEAN; } else { try { + if ((flags & BOUNDARY_EFFECT) !== 0) { + flush_boundary_micro_tasks(); + } if (check_dirtiness(current_effect)) { update_effect(current_effect); } @@ -879,12 +886,13 @@ export function flush_sync(fn) { queued_root_effects = root_effects; is_micro_task_queued = false; - flush_before_micro_tasks(); flush_queued_root_effects(previous_queued_root_effects); var result = fn?.(); - flush_after_tasks(); + flush_boundary_micro_tasks(); + flush_post_micro_tasks(); + flush_idle_tasks(); if (queued_root_effects.length > 0 || root_effects.length > 0) { flush_sync(); } diff --git a/packages/svelte/tests/animation-helpers.js b/packages/svelte/tests/animation-helpers.js index 27fb04b46f..e37c2563af 100644 --- a/packages/svelte/tests/animation-helpers.js +++ b/packages/svelte/tests/animation-helpers.js @@ -1,6 +1,6 @@ import { flushSync } from 'svelte'; import { raf as svelte_raf } from 'svelte/internal/client'; -import { queue_after_micro_task } from '../src/internal/client/dom/task.js'; +import { queue_post_micro_task } from '../src/internal/client/dom/task.js'; export const raf = { animations: new Set(), @@ -132,7 +132,7 @@ class Animation { /** @param {() => {}} fn */ set onfinish(fn) { if (this.#duration === 0) { - queue_after_micro_task(fn); + queue_post_micro_task(fn); } else { this.#onfinish = () => { fn();