Skip to content

Commit

Permalink
fix: ensure resume effects are scheduled in topological order (#15012)
Browse files Browse the repository at this point in the history
* fix: ensure resume effects are scheduled in topological order

* fix: ensure resume effects are scheduled in topological order
  • Loading branch information
trueadm authored Jan 14, 2025
1 parent 360ee70 commit 5419610
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/lemon-llamas-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: ensure resume effects are scheduled in topological order
16 changes: 10 additions & 6 deletions packages/svelte/src/internal/client/reactivity/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -602,17 +602,21 @@ export function resume_effect(effect) {
*/
function resume_children(effect, local) {
if ((effect.f & INERT) === 0) return;
effect.f ^= INERT;

// Ensure the effect is marked as clean again so that any dirty child
// effects can schedule themselves for execution
if ((effect.f & CLEAN) === 0) {
effect.f ^= CLEAN;
}

// If a dependency of this effect changed while it was paused,
// apply the change now
// schedule the effect to update
if (check_dirtiness(effect)) {
update_effect(effect);
set_signal_status(effect, DIRTY);
schedule_effect(effect);
}

// Ensure we toggle the flag after possibly updating the effect so that
// each block logic can correctly operate on inert items
effect.f ^= INERT;

var child = effect.first;

while (child !== null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { flushSync } from '../../../../src/index-client.js';
import { test } from '../../test';

export default test({
async test({ assert, raf, target, logs }) {
assert.htmlEqual(
target.innerHTML,
'<button>Toggle</button><div><div>1</div><div>2</div><div>3</div></div>'
);

const btn1 = target.querySelector('button');
btn1?.click();
flushSync();
raf.tick(250);

assert.htmlEqual(
target.innerHTML,
'<button>Toggle</button><div style="opacity: 0.5;"><div>1</div><div>2</div><div>3</div></div>'
);

logs.length = 0;

await Promise.resolve();

flushSync();
raf.tick(500);

assert.htmlEqual(
target.innerHTML,
'<button>Toggle</button><div style=""><div>3</div><div>4</div></div>'
);

assert.deepEqual(logs, ['$effect.pre', '$effect.pre', '$effect', '$effect']);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script>
function fade(_) {
return {
duration: 500,
css: t => `opacity: ${t}`,
}
}
let toggle = $state(true);
let items = $state([ 1, 2, 3 ]);
const handle_toggle = async () => {
toggle = false;
await Promise.resolve();
items = [3, 4];
toggle = true;
};
</script>

<button onclick={handle_toggle}>Toggle</button>

{#if toggle}
<div transition:fade>
{#each items as item (item)}
{(() => {
$effect(() => {
items;
console.log('$effect');
});

$effect.pre(() => {
items;
console.log('$effect.pre');
});
})()}
<div>{item}</div>
{/each}
</div>
{/if}

0 comments on commit 5419610

Please sign in to comment.