Skip to content

Commit fc90420

Browse files
committed
fix: improve nested effect heuristics
1 parent 0eca0ac commit fc90420

File tree

5 files changed

+81
-7
lines changed

5 files changed

+81
-7
lines changed

.changeset/angry-books-jam.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: improve nested effect heuristics

packages/svelte/src/internal/client/runtime.js

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,13 @@ export function schedule_effect(signal, sync) {
624624
}
625625
if ((flags & EFFECT) !== 0) {
626626
current_queued_effects.push(signal);
627+
// Prevent any nested user effects from potentially triggering
628+
// before this effect is scheduled. We know they will be destroyed
629+
// so we can make them inert to avoid having to find them in the
630+
// queue and remove them.
631+
if ((flags & MANAGED) === 0) {
632+
mark_subtree_children_inert(signal, true);
633+
}
627634
} else {
628635
current_queued_pre_and_render_effects.push(signal);
629636
}
@@ -1017,6 +1024,23 @@ export function mutate_store(store, expression, new_value) {
10171024
/**
10181025
* @param {import('./types.js').ComputationSignal} signal
10191026
* @param {boolean} inert
1027+
* @param {Set<import('./types.js').Block>} [visited_blocks]
1028+
* @returns {void}
1029+
*/
1030+
function mark_subtree_children_inert(signal, inert, visited_blocks) {
1031+
const references = signal.r;
1032+
if (references !== null) {
1033+
let i;
1034+
for (i = 0; i < references.length; i++) {
1035+
mark_subtree_inert(references[i], inert, visited_blocks);
1036+
}
1037+
}
1038+
}
1039+
1040+
/**
1041+
* @param {import('./types.js').ComputationSignal} signal
1042+
* @param {boolean} inert
1043+
* @param {Set<import('./types.js').Block>} [visited_blocks]
10201044
* @returns {void}
10211045
*/
10221046
export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) {
@@ -1055,13 +1079,7 @@ export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) {
10551079
}
10561080
}
10571081
}
1058-
const references = signal.r;
1059-
if (references !== null) {
1060-
let i;
1061-
for (i = 0; i < references.length; i++) {
1062-
mark_subtree_inert(references[i], inert, visited_blocks);
1063-
}
1064-
}
1082+
mark_subtree_children_inert(signal, inert, visited_blocks);
10651083
}
10661084

10671085
/**
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
import { log } from './log.js';
4+
5+
export default test({
6+
before_test() {
7+
log.length = 0;
8+
},
9+
10+
async test({ assert, target }) {
11+
const [b1] = target.querySelectorAll('button');
12+
13+
flushSync(() => {
14+
b1?.click();
15+
});
16+
17+
await Promise.resolve();
18+
assert.deepEqual(log, [
19+
'top level',
20+
'inner',
21+
0,
22+
'destroy inner',
23+
undefined,
24+
'destroy outer',
25+
undefined
26+
]);
27+
}
28+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/** @type {any[]} */
2+
export const log = [];
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script>
2+
import { log } from './log.js';
3+
let c = $state({ a: 0 });
4+
5+
$effect(() => {
6+
log.push('top level')
7+
$effect(() => {
8+
if (c) {
9+
$effect(() => {
10+
log.push('inner',c.a);
11+
return () => log.push('destroy inner', c?.a);
12+
});
13+
}
14+
return () => log.push('destroy outer', c?.a);
15+
})
16+
});
17+
</script>
18+
19+
<button onclick={() => {
20+
c.a = 1; c = null
21+
}}>toggle</button>

0 commit comments

Comments
 (0)