diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 75cd75da8f..3f8fe421ed 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -699,14 +699,25 @@ export function schedule_effect(signal) { export function suspend() { var boundary = get_pending_boundary(); var batch = /** @type {Batch} */ (current_batch); + // In case the pending snippet is shown, we want to update the UI immediately + // and not have the batch be blocked on async work, + // since the async work is happening "hidden" behind the pending snippet. + var ignore_async = boundary.pending; boundary.update_pending_count(1); - batch.increment(); + if (!ignore_async) batch.increment(); return function unsuspend() { boundary.update_pending_count(-1); batch.activate(); - batch.decrement(); + if (ignore_async) { + // Template could have created new effects (for example via attachments) which need to be flushed + Batch.enqueue(() => { + batch.flush(); + }); + } else { + batch.decrement(); + } unset_context(); }; diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 96947ef0fc..78f7c3ca64 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, batch_deriveds, current_batch } from './batch.js'; import { unset_context } from './async.js'; /** @type {Effect | null} */ @@ -135,10 +134,14 @@ export function async_derived(fn, location) { prev = promise; var batch = /** @type {Batch} */ (current_batch); + // In case the pending snippet is shown, we want to update the UI immediately + // and not have the batch be blocked on async work, + // since the async work is happening "hidden" behind the pending snippet. + var ignore_async = boundary.pending; if (should_suspend) { boundary.update_pending_count(1); - batch.increment(); + if (!ignore_async) batch.increment(); } /** @@ -180,7 +183,14 @@ export function async_derived(fn, location) { if (should_suspend) { boundary.update_pending_count(-1); - batch.decrement(); + if (ignore_async) { + // Template could have created new effects (for example via attachments) which need to be flushed + Batch.enqueue(() => { + batch.flush(); + }); + } else { + batch.decrement(); + } } unset_context(); diff --git a/packages/svelte/tests/runtime-runes/samples/async-pending-flushed-immediately/_config.js b/packages/svelte/tests/runtime-runes/samples/async-pending-flushed-immediately/_config.js new file mode 100644 index 0000000000..7c16efc8b7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-pending-flushed-immediately/_config.js @@ -0,0 +1,75 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [increment, resolve] = target.querySelectorAll('button'); + + assert.htmlEqual( + target.innerHTML, + ` + + + ` + ); + + increment.click(); + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` + + + ` + ); + + increment.click(); + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` + + +
loading...
+ ` + ); + + resolve.click(); + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` + + +2
+ ` + ); + + increment.click(); + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` + + +2
+ ` + ); + + resolve.click(); + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` + + +3
+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-pending-flushed-immediately/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-pending-flushed-immediately/main.svelte new file mode 100644 index 0000000000..9c3700c78e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-pending-flushed-immediately/main.svelte @@ -0,0 +1,24 @@ + + + + + +{#if count > 1} +{await push(count)}
+ + {#snippet pending()} +loading...
+ {/snippet} +