diff --git a/.changeset/four-signs-listen.md b/.changeset/four-signs-listen.md new file mode 100644 index 0000000000..b9e089fd12 --- /dev/null +++ b/.changeset/four-signs-listen.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: batch resolution of async work diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 61262a6576..90a695551a 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -102,6 +102,7 @@ export class Boundary { #local_pending_count = 0; #pending_count = 0; + #pending_count_update_queued = false; #is_creating_fallback = false; @@ -202,12 +203,11 @@ export class Boundary { #hydrate_pending_content() { const pending = this.#props.pending; - if (!pending) { - return; - } + if (!pending) return; + this.#pending_effect = branch(() => pending(this.#anchor)); - Batch.enqueue(() => { + queue_micro_task(() => { var anchor = this.#get_anchor(); this.#main_effect = this.#run(() => { @@ -359,9 +359,15 @@ export class Boundary { this.#local_pending_count += d; - if (this.#effect_pending) { - internal_set(this.#effect_pending, this.#local_pending_count); - } + if (!this.#effect_pending || this.#pending_count_update_queued) return; + this.#pending_count_update_queued = true; + + queue_micro_task(() => { + this.#pending_count_update_queued = false; + if (this.#effect_pending) { + internal_set(this.#effect_pending, this.#local_pending_count); + } + }); } get_effect_pending() { diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 3c510f81d4..c4caf8a20b 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -138,6 +138,8 @@ export class Batch { is_fork = false; + #decrement_queued = false; + is_deferred() { return this.is_fork || this.#blocking_pending > 0; } @@ -426,7 +428,22 @@ export class Batch { this.#pending -= 1; if (blocking) this.#blocking_pending -= 1; - this.revive(); + if (this.#decrement_queued) return; + this.#decrement_queued = true; + + queue_micro_task(() => { + this.#decrement_queued = false; + + if (!this.is_deferred()) { + // we only reschedule previously-deferred effects if we expect + // to be able to run them after processing the batch + this.revive(); + } else if (queued_root_effects.length > 0) { + // if other effects are scheduled, process the batch _without_ + // rescheduling the previously-deferred effects + this.flush(); + } + }); } revive() { @@ -464,7 +481,7 @@ export class Batch { batches.add(current_batch); if (!is_flushing_sync) { - Batch.enqueue(() => { + queue_micro_task(() => { if (current_batch !== batch) { // a flushSync happened in the meantime return; @@ -478,11 +495,6 @@ export class Batch { return current_batch; } - /** @param {() => void} task */ - static enqueue(task) { - queue_micro_task(task); - } - apply() { if (!async_mode_flag || (!this.is_fork && batches.size === 1)) return;