@@ -68,6 +68,7 @@ import {
68
68
markRootSuspendedAtTime ,
69
69
markRootFinishedAtTime ,
70
70
markRootUpdatedAtTime ,
71
+ markRootExpiredAtTime ,
71
72
} from './ReactFiberRoot' ;
72
73
import {
73
74
NoMode ,
@@ -521,10 +522,14 @@ function getNextRootExpirationTimeToWorkOn(root: FiberRoot): ExpirationTime {
521
522
// Determines the next expiration time that the root should render, taking
522
523
// into account levels that may be suspended, or levels that may have
523
524
// received a ping.
524
- //
525
+
526
+ const lastExpiredTime = root . lastExpiredTime ;
527
+ if ( lastExpiredTime !== NoWork ) {
528
+ return lastExpiredTime ;
529
+ }
530
+
525
531
// "Pending" refers to any update that hasn't committed yet, including if it
526
532
// suspended. The "suspended" range is therefore a subset.
527
-
528
533
const firstPendingTime = root . firstPendingTime ;
529
534
if ( ! isRootSuspendedAtTime ( root , firstPendingTime ) ) {
530
535
// The highest priority pending time is not suspended. Let's work on that.
@@ -547,6 +552,17 @@ function getNextRootExpirationTimeToWorkOn(root: FiberRoot): ExpirationTime {
547
552
// the next level that the root has work on. This function is called on every
548
553
// update, and right before exiting a task.
549
554
function ensureRootIsScheduled ( root : FiberRoot ) {
555
+ const lastExpiredTime = root . lastExpiredTime ;
556
+ if ( lastExpiredTime !== NoWork ) {
557
+ // Special case: Expired work should flush synchronously.
558
+ root . callbackExpirationTime = Sync ;
559
+ root . callbackPriority = ImmediatePriority ;
560
+ root . callbackNode = scheduleSyncCallback (
561
+ performSyncWorkOnRoot . bind ( null , root , lastExpiredTime ) ,
562
+ ) ;
563
+ return ;
564
+ }
565
+
550
566
const expirationTime = getNextRootExpirationTimeToWorkOn ( root ) ;
551
567
const existingCallbackNode = root . callbackNode ;
552
568
if ( expirationTime === NoWork ) {
@@ -621,20 +637,16 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
621
637
// event time. The next update will compute a new event time.
622
638
currentEventTime = NoWork ;
623
639
640
+ if ( didTimeout ) {
641
+ // An async update expired.
642
+ const currentTime = requestCurrentTime ( ) ;
643
+ markRootExpiredAtTime ( root , currentTime ) ;
644
+ }
645
+
624
646
// Determine the next expiration time to work on, using the fields stored
625
647
// on the root.
626
- let expirationTime = getNextRootExpirationTimeToWorkOn ( root ) ;
648
+ const expirationTime = getNextRootExpirationTimeToWorkOn ( root ) ;
627
649
if ( expirationTime !== NoWork ) {
628
- if ( didTimeout ) {
629
- // An async update expired. There may be other expired updates on
630
- // this root.
631
- const currentTime = requestCurrentTime ( ) ;
632
- if ( currentTime < expirationTime ) {
633
- // Render all the expired work in a single batch.
634
- expirationTime = currentTime ;
635
- }
636
- }
637
-
638
650
const originalCallbackNode = root . callbackNode ;
639
651
try {
640
652
renderRoot ( root , expirationTime , didTimeout ) ;
@@ -673,7 +685,9 @@ export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {
673
685
'means you attempted to commit from inside a lifecycle method.' ,
674
686
) ;
675
687
}
676
- performSyncWorkOnRoot ( root , expirationTime ) ;
688
+ markRootExpiredAtTime ( root , expirationTime ) ;
689
+ ensureRootIsScheduled ( root ) ;
690
+ flushSyncCallbackQueue ( ) ;
677
691
}
678
692
679
693
export function flushDiscreteUpdates ( ) {
@@ -741,9 +755,8 @@ function flushPendingDiscreteUpdates() {
741
755
const roots = rootsWithPendingDiscreteUpdates ;
742
756
rootsWithPendingDiscreteUpdates = null ;
743
757
roots . forEach ( ( expirationTime , root ) => {
744
- scheduleSyncCallback (
745
- performSyncWorkOnRoot . bind ( null , root , expirationTime ) ,
746
- ) ;
758
+ markRootExpiredAtTime ( root , expirationTime ) ;
759
+ ensureRootIsScheduled ( root ) ;
747
760
} ) ;
748
761
// Now flush the immediate queue.
749
762
flushSyncCallbackQueue ( ) ;
@@ -1032,7 +1045,7 @@ function renderRoot(
1032
1045
// synchronously, to see if the error goes away. If there are lower
1033
1046
// priority updates, let's include those, too, in case they fix the
1034
1047
// inconsistency. Render at Idle to include all updates.
1035
- performSyncWorkOnRoot ( root , Idle ) ;
1048
+ markRootExpiredAtTime ( root , Idle ) ;
1036
1049
return ;
1037
1050
}
1038
1051
// Commit the root in its errored state.
0 commit comments