Skip to content

Commit bde51ed

Browse files
authored
fix: don't fail on flushSync while flushing effects (#16674)
* fix: don't fail on `flushSync` while flushing effects WIP fixes #16640 * cleanup, unrelated lint * revert * fix
1 parent 5314f48 commit bde51ed

File tree

6 files changed

+43
-3
lines changed

6 files changed

+43
-3
lines changed

.changeset/honest-coins-work.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: don't fail on `flushSync` while flushing effects

packages/svelte/elements.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1268,7 +1268,7 @@ export interface HTMLMetaAttributes extends HTMLAttributes<HTMLMetaElement> {
12681268
charset?: string | undefined | null;
12691269
content?: string | undefined | null;
12701270
'http-equiv'?:
1271-
| 'accept-ch'
1271+
| 'accept-ch'
12721272
| 'content-security-policy'
12731273
| 'content-type'
12741274
| 'default-style'

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export class Batch {
187187
// if there are multiple batches, we are 'time travelling' —
188188
// we need to undo the changes belonging to any batch
189189
// other than the current one
190-
if (batches.size > 1) {
190+
if (async_mode_flag && batches.size > 1) {
191191
current_values = new Map();
192192
batch_deriveds = new Map();
193193

@@ -484,6 +484,7 @@ export class Batch {
484484
*/
485485
export function flushSync(fn) {
486486
if (async_mode_flag && active_effect !== null) {
487+
// We disallow this because it creates super-hard to reason about stack trace and because it's generally a bad idea
487488
e.flush_sync_in_effect();
488489
}
489490

@@ -622,7 +623,9 @@ function flush_queued_effects(effects) {
622623
}
623624
}
624625

625-
if (eager_block_effects.length > 0) {
626+
// If update_effect() has a flushSync() in it, we may have flushed another flush_queued_effects(),
627+
// which already handled this logic and did set eager_block_effects to null.
628+
if (eager_block_effects?.length > 0) {
626629
// TODO this feels incorrect! it gets the tests passing
627630
old_values.clear();
628631

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
let { text } = $props();
3+
4+
$effect(() => console.log(text));
5+
</script>
6+
7+
{text}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { async_mode } from '../../../helpers';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
// In legacy mode this succeeds and logs 'hello'
6+
// In async mode this throws an error because flushSync is called inside an effect
7+
async test({ assert, target, logs }) {
8+
assert.htmlEqual(target.innerHTML, `<button>show</button> <div>hello</div>`);
9+
assert.deepEqual(logs, ['hello']);
10+
},
11+
runtime_error: async_mode ? 'flush_sync_in_effect' : undefined
12+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
import { flushSync, mount } from 'svelte'
3+
import Child from './Child.svelte';
4+
5+
let show = $state(false);
6+
</script>
7+
8+
<button onclick={() => show = true}>show</button>
9+
10+
<div {@attach (target) => {
11+
mount(Child, { target, props: { text: 'hello' } });
12+
flushSync();
13+
}}></div>

0 commit comments

Comments
 (0)