diff --git a/.changeset/popular-tips-lie.md b/.changeset/popular-tips-lie.md new file mode 100644 index 0000000000..45bd3e23f3 --- /dev/null +++ b/.changeset/popular-tips-lie.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update `$effect.pending()` immediately after a batch is removed diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 5ed0515210..5e678ab113 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -22,7 +22,7 @@ import { get_next_sibling } from '../operations.js'; import { queue_micro_task } from '../task.js'; import * as e from '../../errors.js'; import { DEV } from 'esm-env'; -import { Batch } from '../../reactivity/batch.js'; +import { Batch, effect_pending_updates } from '../../reactivity/batch.js'; import { internal_set, source } from '../../reactivity/sources.js'; import { tag } from '../../dev/tracing.js'; import { createSubscriber } from '../../../../reactivity/create-subscriber.js'; @@ -92,6 +92,12 @@ export class Boundary { */ #effect_pending = null; + #effect_pending_update = () => { + if (this.#effect_pending) { + internal_set(this.#effect_pending, this.#pending_count); + } + }; + #effect_pending_subscriber = createSubscriber(() => { this.#effect_pending = source(this.#pending_count); @@ -238,11 +244,7 @@ export class Boundary { this.parent.#update_pending_count(d); } - queueMicrotask(() => { - if (this.#effect_pending) { - internal_set(this.#effect_pending, this.#pending_count); - } - }); + effect_pending_updates.add(this.#effect_pending_update); } get_effect_pending() { diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 1126946ce9..f881330e90 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -49,6 +49,9 @@ export let batch_deriveds = null; /** @type {Effect[]} Stack of effects, dev only */ export let dev_effect_stack = []; +/** @type {Set<() => void>} */ +export let effect_pending_updates = new Set(); + /** @type {Effect[]} */ let queued_root_effects = []; @@ -296,6 +299,16 @@ export class Batch { deactivate() { current_batch = null; + + for (const update of effect_pending_updates) { + effect_pending_updates.delete(update); + update(); + + if (current_batch !== null) { + // only do one at a time + break; + } + } } neuter() { @@ -319,7 +332,7 @@ export class Batch { batches.delete(this); } - current_batch = null; + this.deactivate(); } flush_effects() { @@ -389,6 +402,8 @@ export class Batch { this.#effects = []; this.flush(); + } else { + this.deactivate(); } } diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-pending/_config.js b/packages/svelte/tests/runtime-runes/samples/async-effect-pending/_config.js new file mode 100644 index 0000000000..9df3620798 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-pending/_config.js @@ -0,0 +1,81 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [increment, shift] = target.querySelectorAll('button'); + + shift.click(); + shift.click(); + shift.click(); + + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +
0
+0
+0
+pending: 0
+ ` + ); + + increment.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +0
+0
+0
+pending: 3
+ ` + ); + + shift.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +0
+0
+0
+pending: 2
+ ` + ); + + shift.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +0
+0
+0
+pending: 1
+ ` + ); + + shift.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +1
+1
+1
+pending: 0
+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-pending/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-pending/main.svelte new file mode 100644 index 0000000000..89cead2cc6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-pending/main.svelte @@ -0,0 +1,32 @@ + + + + + +{await push(value)}
+{await push(value)}
+{await push(value)}
+ +pending: {$effect.pending()}
+ + {#snippet pending()} +loading...
+ {/snippet} +