Skip to content

Commit 3d731c7

Browse files
committed
scheduling
1 parent 0f620e8 commit 3d731c7

File tree

7 files changed

+23
-27
lines changed

7 files changed

+23
-27
lines changed

packages/svelte/src/internal/client/dom/task.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { run_all } from '../../shared/utils.js';
2+
import { is_flushing_sync } from '../reactivity/batch.js';
23

34
// Fallback for when requestIdleCallback is not available
45
const request_idle_callback =
@@ -24,11 +25,15 @@ function run_idle_tasks() {
2425
run_all(tasks);
2526
}
2627

28+
export function has_pending_tasks() {
29+
return micro_tasks.length > 0 || idle_tasks.length > 0;
30+
}
31+
2732
/**
2833
* @param {() => void} fn
2934
*/
3035
export function queue_micro_task(fn) {
31-
if (micro_tasks.length === 0) {
36+
if (micro_tasks.length === 0 && !is_flushing_sync) {
3237
queueMicrotask(run_micro_tasks);
3338
}
3439

packages/svelte/src/internal/client/reactivity/batch.js

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
update_effect
2626
} from '../runtime.js';
2727
import * as e from '../errors.js';
28-
import { flush_tasks } from '../dom/task.js';
28+
import { flush_tasks, has_pending_tasks, queue_micro_task } from '../dom/task.js';
2929
import { DEV } from 'esm-env';
3030
import { invoke_error_boundary } from '../error-handling.js';
3131
import { old_values } from './sources.js';
@@ -56,27 +56,14 @@ export let batch_deriveds = null;
5656
/** @type {Set<() => void>} */
5757
export let effect_pending_updates = new Set();
5858

59-
/** @type {Array<() => void>} */
60-
let tasks = [];
61-
62-
function dequeue() {
63-
const task = /** @type {() => void} */ (tasks.shift());
64-
65-
if (tasks.length > 0) {
66-
queueMicrotask(dequeue);
67-
}
68-
69-
task();
70-
}
71-
7259
/** @type {Effect[]} */
7360
let queued_root_effects = [];
7461

7562
/** @type {Effect | null} */
7663
let last_scheduled_effect = null;
7764

7865
let is_flushing = false;
79-
let is_flushing_sync = false;
66+
export let is_flushing_sync = false;
8067

8168
export class Batch {
8269
/**
@@ -418,6 +405,8 @@ export class Batch {
418405

419406
if (this.#pending === 0) {
420407
Batch.enqueue(() => {
408+
this.activate();
409+
421410
for (const e of this.#dirty_effects) {
422411
set_signal_status(e, DIRTY);
423412
schedule_effect(e);
@@ -469,11 +458,7 @@ export class Batch {
469458

470459
/** @param {() => void} task */
471460
static enqueue(task) {
472-
if (tasks.length === 0) {
473-
queueMicrotask(dequeue);
474-
}
475-
476-
tasks.unshift(task);
461+
queue_micro_task(task);
477462
}
478463
}
479464

@@ -504,7 +489,7 @@ export function flushSync(fn) {
504489
while (true) {
505490
flush_tasks();
506491

507-
if (queued_root_effects.length === 0) {
492+
if (queued_root_effects.length === 0 && !has_pending_tasks()) {
508493
current_batch?.flush();
509494

510495
// we need to check again, in case we just updated an `$effect.pending()`
@@ -690,5 +675,4 @@ export function suspend() {
690675
*/
691676
export function clear() {
692677
batches.clear();
693-
tasks.length = 0;
694678
}

packages/svelte/tests/runtime-runes/samples/await-pending-wait/_config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export default test({
55
async test({ assert, target }) {
66
const [b1, b2, b3] = target.querySelectorAll('button');
77

8+
await Promise.resolve();
9+
810
// not flushing means we wait a tick before showing the pending state ...
911
b2.click();
1012
await Promise.resolve();
@@ -45,6 +47,7 @@ export default test({
4547
);
4648

4749
// when not flushing ...
50+
await Promise.resolve();
4851
b3.click();
4952
await Promise.resolve();
5053
assert.htmlEqual(

packages/svelte/tests/runtime-runes/samples/await-resolve-2/_config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { test } from '../../test';
33
export default test({
44
async test({ assert, target, logs }) {
55
const [b1, b2, b3, b4] = target.querySelectorAll('button');
6+
await Promise.resolve();
67
b1.click();
78
await Promise.resolve();
89
b2.click();

packages/svelte/tests/runtime-runes/samples/await-resolve/_config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { test } from '../../test';
33
export default test({
44
async test({ assert, target, logs }) {
55
const [b1, b2] = target.querySelectorAll('button');
6+
await Promise.resolve();
67
b1.click();
78
await Promise.resolve();
89
assert.htmlEqual(

packages/svelte/tests/runtime-runes/samples/dynamic-component-transition/_config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ export default test({
55
async test({ assert, target, raf }) {
66
const btn = target.querySelector('button');
77

8-
raf.tick(0);
8+
// one tick to not be at 0. Else the flushSync would revert the in-transition which hasn't started, and directly remove the button
9+
raf.tick(1);
910

1011
flushSync(() => {
1112
btn?.click();
1213
});
1314

1415
assert.htmlEqual(target.innerHTML, `<h1>Outside</h1><button style="opacity: 0;">Hide</button>`);
1516

16-
raf.tick(100);
17+
raf.tick(101);
1718

1819
assert.htmlEqual(target.innerHTML, `<h1>Outside</h1>`);
1920
}

packages/svelte/tests/runtime-runes/samples/dynamic-if-component-transition/_config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ export default test({
55
async test({ assert, target, raf }) {
66
const btn = target.querySelector('button');
77

8-
raf.tick(0);
8+
// one tick to not be at 0. Else the flushSync would revert the in-transition which hasn't started, and directly remove the button
9+
raf.tick(1);
910

1011
flushSync(() => {
1112
btn?.click();
1213
});
1314

1415
assert.htmlEqual(target.innerHTML, `<h1>Outside</h1><button style="opacity: 0;">Hide</button>`);
1516

16-
raf.tick(100);
17+
raf.tick(101);
1718

1819
assert.htmlEqual(target.innerHTML, `<h1>Outside</h1>`);
1920
}

0 commit comments

Comments
 (0)