@@ -157,6 +157,7 @@ export interface Chore<T extends ChoreType = ChoreType> {
157157 $blockedChores$ : ChoreArray | null ;
158158 $startTime$ : number | undefined ;
159159 $endTime$ : number | undefined ;
160+ $blocksJournalFlush$ : boolean ;
160161
161162 $resolve$ : ( ( value : any ) => void ) | undefined ;
162163 $reject$ : ( ( reason ?: any ) => void ) | undefined ;
@@ -194,6 +195,8 @@ export const createScheduler = (
194195 let currentTime = performance . now ( ) ;
195196 const nextTick = createNextTick ( drainChoreQueue ) ;
196197 let flushTimerId : number | null = null ;
198+ let blockingChoresCount = 0 ;
199+ let currentChore : Chore | null = null ;
197200
198201 function drainInNextTick ( ) {
199202 if ( ! drainScheduled ) {
@@ -275,6 +278,10 @@ export const createScheduler = (
275278 if ( isTask ) {
276279 ( hostOrTask as Task ) . $flags$ |= TaskFlags . DIRTY ;
277280 }
281+ // Determine if this chore should block journal flush
282+ // NODE_DIFF and COMPONENT always block journal flush
283+ const shouldBlockJournalFlush = type === ChoreType . NODE_DIFF || type === ChoreType . COMPONENT ;
284+
278285 const chore : Chore < T > = {
279286 $type$ : type ,
280287 $idx$ : isTask
@@ -289,11 +296,17 @@ export const createScheduler = (
289296 $blockedChores$ : null ,
290297 $startTime$ : undefined ,
291298 $endTime$ : undefined ,
299+ $blocksJournalFlush$ : shouldBlockJournalFlush ,
292300 $resolve$ : undefined ,
293301 $reject$ : undefined ,
294302 $returnValue$ : null ! ,
295303 } ;
296304
305+ // Increment blocking counter as soon as chore is created (even if it gets blocked)
306+ if ( shouldBlockJournalFlush ) {
307+ blockingChoresCount ++ ;
308+ }
309+
297310 if ( type === ChoreType . WAIT_FOR_QUEUE ) {
298311 getChorePromise ( chore ) ;
299312 drainChore = chore as Chore < ChoreType . WAIT_FOR_QUEUE > ;
@@ -345,6 +358,10 @@ This is often caused by modifying a signal in an already rendered component duri
345358 logWarn ( warningMessage ) ;
346359 DEBUG &&
347360 debugTrace ( 'schedule.SKIPPED host is not updatable' , chore , choreQueue , blockedChores ) ;
361+ // Decrement counter if this was a blocking chore that we're skipping
362+ if ( chore . $blocksJournalFlush$ ) {
363+ blockingChoresCount -- ;
364+ }
348365 return chore ;
349366 }
350367 }
@@ -374,7 +391,14 @@ This is often caused by modifying a signal in an already rendered component duri
374391 }
375392
376393 addChore ( chore , choreQueue ) ;
377- DEBUG && debugTrace ( 'schedule' , chore , choreQueue , blockedChores ) ;
394+
395+ DEBUG &&
396+ debugTrace (
397+ chore . $blocksJournalFlush$ ? `schedule (blocking ${ blockingChoresCount } )` : 'schedule' ,
398+ chore ,
399+ choreQueue ,
400+ blockedChores
401+ ) ;
378402
379403 const runImmediately = ( isServer && type === ChoreType . COMPONENT ) || type === ChoreType . RUN_QRL ;
380404
@@ -432,6 +456,16 @@ This is often caused by modifying a signal in an already rendered component duri
432456 }
433457
434458 function applyJournalFlush ( ) {
459+ if ( blockingChoresCount > 0 ) {
460+ DEBUG &&
461+ debugTrace (
462+ `journalFlush.BLOCKED (${ blockingChoresCount } blocking chores)` ,
463+ null ,
464+ choreQueue ,
465+ blockedChores
466+ ) ;
467+ return ;
468+ }
435469 if ( ! isJournalFlushRunning ) {
436470 // prevent multiple journal flushes from running at the same time
437471 isJournalFlushRunning = true ;
@@ -521,13 +555,12 @@ This is often caused by modifying a signal in an already rendered component duri
521555 }
522556 } ;
523557
524- let currentChore : Chore | null = null ;
525-
526558 try {
527559 while ( choreQueue . length ) {
528560 currentTime = performance . now ( ) ;
529561 const chore = ( currentChore = choreQueue . shift ( ) ! ) ;
530562 if ( chore . $state$ !== ChoreState . NONE ) {
563+ // Chore was already processed, counter already decremented in finishChore/handleError
531564 continue ;
532565 }
533566
@@ -541,6 +574,10 @@ This is often caused by modifying a signal in an already rendered component duri
541574 if ( vnode_isVNode ( chore . $host$ ) ) {
542575 chore . $host$ . chores ?. delete ( chore ) ;
543576 }
577+ // Decrement counter if this was a blocking chore that we're skipping
578+ if ( chore . $blocksJournalFlush$ ) {
579+ blockingChoresCount -- ;
580+ }
544581 continue ;
545582 }
546583
@@ -618,13 +655,40 @@ This is often caused by modifying a signal in an already rendered component duri
618655 if ( vnode_isVNode ( chore . $host$ ) ) {
619656 chore . $host$ . chores ?. delete ( chore ) ;
620657 }
621- DEBUG && debugTrace ( 'execute.DONE' , chore , choreQueue , blockedChores ) ;
658+
659+ // Decrement blocking counter if this chore was blocking journal flush
660+ if ( chore . $blocksJournalFlush$ ) {
661+ blockingChoresCount -- ;
662+ DEBUG &&
663+ debugTrace (
664+ `execute.DONE (blocking ${ blockingChoresCount } )` ,
665+ chore ,
666+ choreQueue ,
667+ blockedChores
668+ ) ;
669+ } else {
670+ DEBUG && debugTrace ( 'execute.DONE' , chore , choreQueue , blockedChores ) ;
671+ }
622672 }
623673
624674 function handleError ( chore : Chore , e : any ) {
625675 chore . $endTime$ = performance . now ( ) ;
626676 chore . $state$ = ChoreState . FAILED ;
627- DEBUG && debugTrace ( 'execute.ERROR' , chore , choreQueue , blockedChores ) ;
677+
678+ // Decrement blocking counter if this chore was blocking journal flush
679+ if ( chore . $blocksJournalFlush$ ) {
680+ blockingChoresCount -- ;
681+ DEBUG &&
682+ debugTrace (
683+ `execute.ERROR (blocking ${ blockingChoresCount } )` ,
684+ chore ,
685+ choreQueue ,
686+ blockedChores
687+ ) ;
688+ } else {
689+ DEBUG && debugTrace ( 'execute.ERROR' , chore , choreQueue , blockedChores ) ;
690+ }
691+
628692 // If we used the result as promise, this won't exist
629693 chore . $reject$ ?.( e ) ;
630694 container . handleError ( e , chore . $host$ ) ;
0 commit comments