@@ -30,6 +30,7 @@ import {
3030 enableSchedulingProfiler ,
3131 enableScopeAPI ,
3232 skipUnmountedBoundaries ,
33+ disableSchedulerTimeoutInWorkLoop ,
3334} from 'shared/ReactFeatureFlags' ;
3435import ReactSharedInternals from 'shared/ReactSharedInternals' ;
3536import invariant from 'shared/invariant' ;
@@ -750,7 +751,7 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
750751
751752// This is the entry point for every concurrent task, i.e. anything that
752753// goes through Scheduler.
753- function performConcurrentWorkOnRoot ( root ) {
754+ function performConcurrentWorkOnRoot ( root , didTimeout ) {
754755 // Since we know we're in a React event, we can clear the current
755756 // event time. The next update will compute a new event time.
756757 currentEventTime = NoTimestamp ;
@@ -790,6 +791,19 @@ function performConcurrentWorkOnRoot(root) {
790791 return null ;
791792 }
792793
794+ // TODO: We only check `didTimeout` defensively, to account for a Scheduler
795+ // bug where `shouldYield` sometimes returns `true` even if `didTimeout` is
796+ // true, which leads to an infinite loop. Once the bug in Scheduler is
797+ // fixed, we can remove this, since we track expiration ourselves.
798+ if (!disableSchedulerTimeoutInWorkLoop && didTimeout ) {
799+ // Something expired. Flush synchronously until there's no expired
800+ // work left.
801+ markRootExpired ( root , lanes ) ;
802+ // This will schedule a synchronous callback.
803+ ensureRootIsScheduled ( root , now ( ) ) ;
804+ return null ;
805+ }
806+
793807 let exitStatus = renderRootConcurrent(root, lanes);
794808
795809 if (
0 commit comments