@@ -194,6 +194,8 @@ export const createScheduler = (
194194 let currentTime = performance . now ( ) ;
195195 const nextTick = createNextTick ( drainChoreQueue ) ;
196196 let flushTimerId : number | null = null ;
197+ let blockingChoresCount = 0 ;
198+ let currentChore : Chore | null = null ;
197199
198200 function drainInNextTick ( ) {
199201 if ( ! drainScheduled ) {
@@ -275,6 +277,7 @@ export const createScheduler = (
275277 if ( isTask ) {
276278 ( hostOrTask as Task ) . $flags$ |= TaskFlags . DIRTY ;
277279 }
280+
278281 const chore : Chore < T > = {
279282 $type$ : type ,
280283 $idx$ : isTask
@@ -345,6 +348,10 @@ This is often caused by modifying a signal in an already rendered component duri
345348 logWarn ( warningMessage ) ;
346349 DEBUG &&
347350 debugTrace ( 'schedule.SKIPPED host is not updatable' , chore , choreQueue , blockedChores ) ;
351+ // Decrement counter if this was a blocking chore that we're skipping
352+ if ( isRenderBlocking ( type ) ) {
353+ blockingChoresCount -- ;
354+ }
348355 return chore ;
349356 }
350357 }
@@ -373,8 +380,15 @@ This is often caused by modifying a signal in an already rendered component duri
373380 }
374381 }
375382
376- addChore ( chore , choreQueue ) ;
377- DEBUG && debugTrace ( 'schedule' , chore , choreQueue , blockedChores ) ;
383+ addChoreAndIncrementBlockingCounter ( chore , choreQueue ) ;
384+
385+ DEBUG &&
386+ debugTrace (
387+ isRenderBlocking ( type ) ? `schedule (blocking ${ blockingChoresCount } )` : 'schedule' ,
388+ chore ,
389+ choreQueue ,
390+ blockedChores
391+ ) ;
378392
379393 const runImmediately = ( isServer && type === ChoreType . COMPONENT ) || type === ChoreType . RUN_QRL ;
380394
@@ -432,6 +446,16 @@ This is often caused by modifying a signal in an already rendered component duri
432446 }
433447
434448 function applyJournalFlush ( ) {
449+ if ( blockingChoresCount > 0 ) {
450+ DEBUG &&
451+ debugTrace (
452+ `journalFlush.BLOCKED (${ blockingChoresCount } blocking chores)` ,
453+ null ,
454+ choreQueue ,
455+ blockedChores
456+ ) ;
457+ return ;
458+ }
435459 if ( ! isJournalFlushRunning ) {
436460 // prevent multiple journal flushes from running at the same time
437461 isJournalFlushRunning = true ;
@@ -509,7 +533,7 @@ This is often caused by modifying a signal in an already rendered component duri
509533 if ( vnode_isVNode ( blockedChore . $host$ ) ) {
510534 blockedChore . $host$ . blockedChores ?. delete ( blockedChore ) ;
511535 }
512- addChore ( blockedChore , choreQueue ) ;
536+ addChoreAndIncrementBlockingCounter ( blockedChore , choreQueue ) ;
513537 DEBUG && debugTrace ( 'schedule.UNBLOCKED' , blockedChore , choreQueue , blockedChores ) ;
514538 blockedChoresScheduled = true ;
515539 }
@@ -521,13 +545,12 @@ This is often caused by modifying a signal in an already rendered component duri
521545 }
522546 } ;
523547
524- let currentChore : Chore | null = null ;
525-
526548 try {
527549 while ( choreQueue . length ) {
528550 currentTime = performance . now ( ) ;
529551 const chore = ( currentChore = choreQueue . shift ( ) ! ) ;
530552 if ( chore . $state$ !== ChoreState . NONE ) {
553+ // Chore was already processed, counter already decremented in finishChore/handleError
531554 continue ;
532555 }
533556
@@ -541,6 +564,10 @@ This is often caused by modifying a signal in an already rendered component duri
541564 if ( vnode_isVNode ( chore . $host$ ) ) {
542565 chore . $host$ . chores ?. delete ( chore ) ;
543566 }
567+ // Decrement counter if this was a blocking chore that we're skipping
568+ if ( isRenderBlocking ( chore . $type$ ) ) {
569+ blockingChoresCount -- ;
570+ }
544571 continue ;
545572 }
546573
@@ -618,13 +645,40 @@ This is often caused by modifying a signal in an already rendered component duri
618645 if ( vnode_isVNode ( chore . $host$ ) ) {
619646 chore . $host$ . chores ?. delete ( chore ) ;
620647 }
621- DEBUG && debugTrace ( 'execute.DONE' , chore , choreQueue , blockedChores ) ;
648+
649+ // Decrement blocking counter if this chore was blocking journal flush
650+ if ( isRenderBlocking ( chore . $type$ ) ) {
651+ blockingChoresCount -- ;
652+ DEBUG &&
653+ debugTrace (
654+ `execute.DONE (blocking ${ blockingChoresCount } )` ,
655+ chore ,
656+ choreQueue ,
657+ blockedChores
658+ ) ;
659+ } else {
660+ DEBUG && debugTrace ( 'execute.DONE' , chore , choreQueue , blockedChores ) ;
661+ }
622662 }
623663
624664 function handleError ( chore : Chore , e : any ) {
625665 chore . $endTime$ = performance . now ( ) ;
626666 chore . $state$ = ChoreState . FAILED ;
627- DEBUG && debugTrace ( 'execute.ERROR' , chore , choreQueue , blockedChores ) ;
667+
668+ // Decrement blocking counter if this chore was blocking journal flush
669+ if ( isRenderBlocking ( chore . $type$ ) ) {
670+ blockingChoresCount -- ;
671+ DEBUG &&
672+ debugTrace (
673+ `execute.ERROR (blocking ${ blockingChoresCount } )` ,
674+ chore ,
675+ choreQueue ,
676+ blockedChores
677+ ) ;
678+ } else {
679+ DEBUG && debugTrace ( 'execute.ERROR' , chore , choreQueue , blockedChores ) ;
680+ }
681+
628682 // If we used the result as promise, this won't exist
629683 chore . $reject$ ?.( e ) ;
630684 container . handleError ( e , chore . $host$ ) ;
@@ -814,8 +868,25 @@ This is often caused by modifying a signal in an already rendered component duri
814868 }
815869 return null ;
816870 }
871+
872+ function addChoreAndIncrementBlockingCounter ( chore : Chore , choreArray : ChoreArray ) {
873+ if ( addChore ( chore , choreArray ) ) {
874+ blockingChoresCount ++ ;
875+ }
876+ }
817877} ;
818878
879+ export function addChore ( chore : Chore , choreArray : ChoreArray ) : boolean {
880+ const idx = choreArray . add ( chore ) ;
881+ if ( idx < 0 ) {
882+ if ( vnode_isVNode ( chore . $host$ ) ) {
883+ ( chore . $host$ . chores ||= new ChoreArray ( ) ) . add ( chore ) ;
884+ }
885+ return isRenderBlocking ( chore . $type$ ) ;
886+ }
887+ return false ;
888+ }
889+
819890function vNodeAlreadyDeleted ( chore : Chore ) : boolean {
820891 return ! ! ( chore . $host$ && vnode_isVNode ( chore . $host$ ) && chore . $host$ . flags & VNodeFlags . Deleted ) ;
821892}
@@ -839,11 +910,8 @@ export function addBlockedChore(
839910 }
840911}
841912
842- export function addChore ( chore : Chore , choreArray : ChoreArray ) {
843- const idx = choreArray . add ( chore ) ;
844- if ( idx < 0 && vnode_isVNode ( chore . $host$ ) ) {
845- ( chore . $host$ . chores ||= new ChoreArray ( ) ) . add ( chore ) ;
846- }
913+ function isRenderBlocking ( type : ChoreType ) : boolean {
914+ return type === ChoreType . NODE_DIFF || type === ChoreType . COMPONENT ;
847915}
848916
849917function choreTypeToName ( type : ChoreType ) : string {
0 commit comments