File tree Expand file tree Collapse file tree 4 files changed +114
-1
lines changed
src/internal/client/reactivity
tests/runtime-runes/samples/async-fork-update-same-state Expand file tree Collapse file tree 4 files changed +114
-1
lines changed Original file line number Diff line number Diff line change 1+ ---
2+ ' svelte ' : patch
3+ ---
4+
5+ fix: ensure deferred effects can be rescheduled later on
Original file line number Diff line number Diff line change @@ -16,7 +16,8 @@ import {
1616 BOUNDARY_EFFECT ,
1717 EAGER_EFFECT ,
1818 HEAD_EFFECT ,
19- ERROR_VALUE
19+ ERROR_VALUE ,
20+ WAS_MARKED
2021} from '#client/constants' ;
2122import { async_mode_flag } from '../../flags/index.js' ;
2223import { deferred , define_property } from '../../shared/utils.js' ;
@@ -274,11 +275,32 @@ export class Batch {
274275 const target = ( e . f & DIRTY ) !== 0 ? this . #dirty_effects : this . #maybe_dirty_effects;
275276 target . push ( e ) ;
276277
278+ // Since we're not executing these effects now, we need to clear any WAS_MARKED flags
279+ // so that other batches can correctly reach these effects during their own traversal
280+ this . #clear_marked( e . deps ) ;
281+
277282 // mark as clean so they get scheduled if they depend on pending async state
278283 set_signal_status ( e , CLEAN ) ;
279284 }
280285 }
281286
287+ /**
288+ * @param {Value[] | null } deps
289+ */
290+ #clear_marked( deps ) {
291+ if ( deps === null ) return ;
292+
293+ for ( const dep of deps ) {
294+ if ( ( dep . f & DERIVED ) === 0 || ( dep . f & WAS_MARKED ) === 0 ) {
295+ continue ;
296+ }
297+
298+ dep . f ^= WAS_MARKED ;
299+
300+ this . #clear_marked( /** @type {Derived } */ ( dep ) . deps ) ;
301+ }
302+ }
303+
282304 /**
283305 * Associate a change to a given source with the current
284306 * batch, noting its previous and current values
Original file line number Diff line number Diff line change 1+ import { tick } from 'svelte' ;
2+ import { test } from '../../test' ;
3+
4+ export default test ( {
5+ async test ( { assert, target, logs } ) {
6+ assert . deepEqual ( logs , [ 0 ] ) ;
7+
8+ const [ fork1 , fork2 , commit ] = target . querySelectorAll ( 'button' ) ;
9+
10+ fork1 . click ( ) ;
11+ await tick ( ) ;
12+ assert . htmlEqual (
13+ target . innerHTML ,
14+ `
15+ <button>fork 1</button>
16+ <button>fork 2</button>
17+ <button>commit</button>
18+ <p>0</p>
19+ `
20+ ) ;
21+ assert . deepEqual ( logs , [ 0 ] ) ;
22+
23+ fork2 . click ( ) ;
24+ await tick ( ) ;
25+ assert . htmlEqual (
26+ target . innerHTML ,
27+ `
28+ <button>fork 1</button>
29+ <button>fork 2</button>
30+ <button>commit</button>
31+ <p>0</p>
32+ `
33+ ) ;
34+ assert . deepEqual ( logs , [ 0 ] ) ;
35+
36+ commit . click ( ) ;
37+ await tick ( ) ;
38+ assert . htmlEqual (
39+ target . innerHTML ,
40+ `
41+ <button>fork 1</button>
42+ <button>fork 2</button>
43+ <button>commit</button>
44+ <p>1</p>
45+ `
46+ ) ;
47+ assert . deepEqual ( logs , [ 0 , 1 ] ) ;
48+ }
49+ } ) ;
Original file line number Diff line number Diff line change 1+ <script >
2+ import { fork } from " svelte" ;
3+
4+ let state = $state (0 );
5+
6+ let count = $derived (state);
7+
8+ $effect .pre (() => {
9+ console .log (count);
10+ });
11+
12+ let forked;
13+ </script >
14+
15+ <button onclick ={()=> {
16+ forked ?.discard ?.();
17+ forked = fork (()=> {
18+ state ++ ;
19+ });
20+ }}>
21+ fork 1
22+ </button >
23+
24+ <button onclick ={()=> {
25+ forked ?.discard ?.();
26+ forked = fork (()=> {
27+ state ++ ;
28+ })
29+ }}>
30+ fork 2
31+ </button >
32+
33+ <button onclick ={()=> {
34+ forked ?.commit ();
35+ }}>commit</button >
36+
37+ <p >{count }</p >
You can’t perform that action at this time.
0 commit comments