@ -15,7 +15,6 @@ import {
ERROR _VALUE ,
MANAGED _EFFECT ,
REACTION _RAN ,
STATE _EAGER _EFFECT ,
DESTROYING
} from '#client/constants' ;
import { async _mode _flag } from '../../flags/index.js' ;
@ -110,6 +109,13 @@ export class Batch {
linked = true ;
/ * *
* In case of a fork with eager effects , this is the related eager batch
* that will commit eagerly updating effects just before this fork commits .
* @ type { Batch | null }
* /
eager = null ;
/** @type {Batch | null} */
# prev = null ;
@ -1242,16 +1248,22 @@ function depends_on(reaction, sources, checked) {
return false ;
}
/** @type { Source<number>[]} */
let eager _ versions = [ ] ;
/** @type { Map<Batch | null, Source<number>[]> } */
let eager _ map = new Map ( ) ;
let running _eager _effect = false ;
function eager _flush ( ) {
/** @param {Batch} batch */
function eager _flush ( batch ) {
flushSync ( ( ) => {
const eager = eager _versions ;
eager _versions = [ ] ;
for ( const version of eager ) {
const versions = /** @type {Source<number>[]} */ ( eager _map . get ( batch ) ) ;
eager _map . delete ( batch ) ;
const eager _batch = ( current _batch = batch . eager ? ? new Batch ( ) ) ;
if ( batch . is _fork ) {
eager _batch . is _fork = true ;
batch . eager = eager _batch ;
}
for ( const version of versions ) {
update ( version ) ;
}
} ) ;
@ -1289,7 +1301,7 @@ export function eager(fn) {
version . label = '<eager>' ;
}
var effect = eager _effect ( ( ) => {
eager _effect ( ( ) => {
if ( initial ) {
// the first time this runs, we create an eager effect
// that will run eagerly whenever the expression changes
@ -1308,23 +1320,18 @@ export function eager(fn) {
return ;
}
if ( ! current _batch ? . is _fork ) {
// the second time this effect runs, it's to schedule a
// `version` update. since this will recreate the effect,
// we don't need to evaluate the expression here
if ( eager _versions . length === 0 ) {
queue _micro _task ( eager _flush ) ;
}
eager _versions . push ( version ) ;
} else {
fn ( ) ;
// the second time this effect runs, it's to schedule a
// `version` update. since this will recreate the effect,
// we don't need to evaluate the expression here
const batch = /** @type {Batch} */ ( current _batch ) ;
const versions = eager _map . get ( batch ) ? ? [ ] ;
if ( versions . length === 0 ) {
eager _map . set ( batch , versions ) ;
queue _micro _task ( ( ) => eager _flush ( batch ) ) ;
}
} ) ;
// TODO ideally this wouldn't be necessary. I haven't figured out a way for these
// effects to correctly be marked dirty when `$state.eager(...)` arguments change
effect . f |= STATE _EAGER _EFFECT ;
versions . push ( version ) ;
} ) ;
initial = false ;
@ -1430,24 +1437,26 @@ export function fork(fn) {
value . wv = snapshot . wv ;
}
// trigger any `$state.eager(...)` expressions with the new state.
// eager effects don't get scheduled like other effects, so we
// can't just encounter them during traversal, we need to
// proactively flush them
// TODO maybe there's a better implementation?
// e.g. maybe we can just schedule them so that they run
// with everything else during batch.flush?
flushSync ( ( ) => {
/** @type {Set<Effect>} */
var eager _effects = new Set ( ) ;
for ( var source of batch . current . keys ( ) ) {
mark _eager _effects ( source , eager _effects ) ;
// Flush the eager batch first to update any effects depending
// on `$state.eager/$effect.pending`
if ( batch . eager ) {
const eager = batch . eager ;
eager . is _fork = false ;
for ( [ reaction , cv ] of eager . cvs ) {
if ( cv > reaction . cv ) {
reaction . cv = cv ;
}
}
set _eager _effects ( eager _effects ) ;
flush _eager _effects ( ) ;
} ) ;
for ( [ value , snapshot ] of eager . current ) {
value . v = snapshot . v ;
value . wv = snapshot . wv ;
}
eager . flush ( ) ;
batch . eager = null ;
}
batch . flush ( ) ;
await settled ;
@ -1460,6 +1469,7 @@ export function fork(fn) {
}
if ( ! committed && batch . linked ) {
batch . eager ? . discard ( ) ;
batch . discard ( ) ;
}
}