@@ -15,8 +15,18 @@ import type {Interaction} from 'scheduler/src/Tracing';
1515import {
1616 __interactionsRef ,
1717 __subscriberRef ,
18- unstable_wrap as Schedule_tracing_wrap ,
18+ unstable_wrap as Scheduler_tracing_wrap ,
1919} from 'scheduler/tracing' ;
20+ import {
21+ unstable_next as Scheduler_next ,
22+ unstable_getCurrentPriorityLevel as getCurrentPriorityLevel ,
23+ unstable_runWithPriority as runWithPriority ,
24+ unstable_ImmediatePriority as ImmediatePriority ,
25+ unstable_UserBlockingPriority as UserBlockingPriority ,
26+ unstable_NormalPriority as NormalPriority ,
27+ unstable_LowPriority as LowPriority ,
28+ unstable_IdlePriority as IdlePriority ,
29+ } from 'scheduler' ;
2030import {
2131 invokeGuardedCallback ,
2232 hasCaughtError ,
@@ -122,7 +132,7 @@ import {
122132 computeAsyncExpiration ,
123133 computeInteractiveExpiration ,
124134} from './ReactFiberExpirationTime' ;
125- import { ConcurrentMode , ProfileMode } from './ReactTypeOfMode' ;
135+ import { ConcurrentMode , ProfileMode , NoContext } from './ReactTypeOfMode' ;
126136import { enqueueUpdate , resetCurrentlyProcessingQueue } from './ReactUpdateQueue' ;
127137import { createCapturedValue } from './ReactCapturedValue' ;
128138import {
@@ -242,11 +252,6 @@ if (__DEV__) {
242252// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing.
243253let lastUniqueAsyncExpiration : number = Sync - 1 ;
244254
245- // Represents the expiration time that incoming updates should use. (If this
246- // is NoWork, use the default strategy: async updates in async mode, sync
247- // updates in sync mode.)
248- let expirationContext : ExpirationTime = NoWork ;
249-
250255let isWorking : boolean = false ;
251256
252257// The next work in progress fiber that we're currently working on.
@@ -793,9 +798,11 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
793798 // TODO: Avoid this extra callback by mutating the tracing ref directly,
794799 // like we do at the beginning of commitRoot. I've opted not to do that
795800 // here because that code is still in flux.
796- callback = Schedule_tracing_wrap ( callback ) ;
801+ callback = Scheduler_tracing_wrap ( callback ) ;
797802 }
798- passiveEffectCallbackHandle = schedulePassiveEffects ( callback ) ;
803+ passiveEffectCallbackHandle = runWithPriority ( NormalPriority , ( ) => {
804+ return schedulePassiveEffects ( callback ) ;
805+ } ) ;
799806 passiveEffectCallback = callback ;
800807 }
801808
@@ -1579,52 +1586,58 @@ function computeUniqueAsyncExpiration(): ExpirationTime {
15791586}
15801587
15811588function computeExpirationForFiber ( currentTime : ExpirationTime , fiber : Fiber ) {
1589+ const priorityLevel = getCurrentPriorityLevel ( ) ;
1590+
15821591 let expirationTime ;
1583- if ( expirationContext !== NoWork ) {
1584- // An explicit expiration context was set;
1585- expirationTime = expirationContext ;
1586- } else if ( isWorking ) {
1587- if ( isCommitting ) {
1588- // Updates that occur during the commit phase should have sync priority
1589- // by default.
1590- expirationTime = Sync ;
1591- } else {
1592- // Updates during the render phase should expire at the same time as
1593- // the work that is being rendered.
1594- expirationTime = nextRenderExpirationTime ;
1595- }
1592+ if ( ( fiber . mode & ConcurrentMode ) === NoContext ) {
1593+ // Outside of concurrent mode, updates are always synchronous.
1594+ expirationTime = Sync ;
1595+ } else if ( isWorking && ! isCommitting ) {
1596+ // During render phase, updates expire during as the current render.
1597+ expirationTime = nextRenderExpirationTime ;
15961598 } else {
1597- // No explicit expiration context was set, and we're not currently
1598- // performing work. Calculate a new expiration time.
1599- if ( fiber . mode & ConcurrentMode ) {
1600- if ( isBatchingInteractiveUpdates ) {
1601- // This is an interactive update
1599+ switch ( priorityLevel ) {
1600+ case ImmediatePriority :
1601+ expirationTime = Sync ;
1602+ break ;
1603+ case UserBlockingPriority :
16021604 expirationTime = computeInteractiveExpiration ( currentTime ) ;
1603- } else {
1604- // This is an async update
1605+ break ;
1606+ case NormalPriority :
1607+ // This is a normal, concurrent update
16051608 expirationTime = computeAsyncExpiration ( currentTime ) ;
1606- }
1607- // If we're in the middle of rendering a tree, do not update at the same
1608- // expiration time that is already rendering.
1609- if ( nextRoot !== null && expirationTime === nextRenderExpirationTime ) {
1610- expirationTime -= 1 ;
1611- }
1612- } else {
1613- // This is a sync update
1614- expirationTime = Sync ;
1609+ break ;
1610+ case LowPriority :
1611+ case IdlePriority :
1612+ expirationTime = Never ;
1613+ break ;
1614+ default :
1615+ invariant (
1616+ false ,
1617+ 'Unknown priority level. This error is likely caused by a bug in ' +
1618+ 'React. Please file an issue.' ,
1619+ ) ;
16151620 }
1616- }
1617- if ( isBatchingInteractiveUpdates ) {
1618- // This is an interactive update. Keep track of the lowest pending
1619- // interactive expiration time. This allows us to synchronously flush
1620- // all interactive updates when needed.
1621- if (
1622- lowestPriorityPendingInteractiveExpirationTime === NoWork ||
1623- expirationTime < lowestPriorityPendingInteractiveExpirationTime
1624- ) {
1625- lowestPriorityPendingInteractiveExpirationTime = expirationTime ;
1621+
1622+ // If we're in the middle of rendering a tree, do not update at the same
1623+ // expiration time that is already rendering.
1624+ if ( nextRoot !== null && expirationTime === nextRenderExpirationTime ) {
1625+ expirationTime -= 1 ;
16261626 }
16271627 }
1628+
1629+ // Keep track of the lowest pending interactive expiration time. This
1630+ // allows us to synchronously flush all interactive updates
1631+ // when needed.
1632+ // TODO: Move this to renderer?
1633+ if (
1634+ priorityLevel === UserBlockingPriority &&
1635+ ( lowestPriorityPendingInteractiveExpirationTime === NoWork ||
1636+ expirationTime < lowestPriorityPendingInteractiveExpirationTime )
1637+ ) {
1638+ lowestPriorityPendingInteractiveExpirationTime = expirationTime ;
1639+ }
1640+
16281641 return expirationTime ;
16291642}
16301643
@@ -1862,34 +1875,16 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
18621875 }
18631876}
18641877
1865- function deferredUpdates < A > (fn: () => A ) : A {
1866- const currentTime = requestCurrentTime ( ) ;
1867- const previousExpirationContext = expirationContext ;
1868- const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates ;
1869- expirationContext = computeAsyncExpiration ( currentTime ) ;
1870- isBatchingInteractiveUpdates = false ;
1871- try {
1872- return fn ( ) ;
1873- } finally {
1874- expirationContext = previousExpirationContext ;
1875- isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates ;
1876- }
1877- }
1878-
18791878function syncUpdates < A , B , C0 , D , R > (
18801879 fn: (A, B, C0, D) => R ,
18811880 a : A ,
18821881 b : B ,
18831882 c : C0 ,
18841883 d : D ,
18851884) : R {
1886- const previousExpirationContext = expirationContext ;
1887- expirationContext = Sync ;
1888- try {
1885+ return runWithPriority ( ImmediatePriority , ( ) => {
18891886 return fn ( a , b , c , d ) ;
1890- } finally {
1891- expirationContext = previousExpirationContext ;
1892- }
1887+ } ) ;
18931888}
18941889
18951890// TODO: Everything below this is written as if it has been lifted to the
@@ -1910,7 +1905,6 @@ let unhandledError: mixed | null = null;
19101905
19111906let isBatchingUpdates: boolean = false;
19121907let isUnbatchingUpdates: boolean = false;
1913- let isBatchingInteractiveUpdates : boolean = false ;
19141908
19151909let completedBatches: Array< Batch > | null = null;
19161910
@@ -2441,7 +2435,9 @@ function completeRoot(
24412435 lastCommittedRootDuringThisBatch = root ;
24422436 nestedUpdateCount = 0 ;
24432437 }
2444- commitRoot ( root , finishedWork ) ;
2438+ runWithPriority ( ImmediatePriority , ( ) => {
2439+ commitRoot ( root , finishedWork ) ;
2440+ } ) ;
24452441}
24462442
24472443function onUncaughtError ( error : mixed ) {
@@ -2507,9 +2503,6 @@ function flushSync<A, R>(fn: (a: A) => R, a: A): R {
25072503}
25082504
25092505function interactiveUpdates < A , B , R > ( fn : ( A , B ) = > R , a : A , b : B ) : R {
2510- if ( isBatchingInteractiveUpdates ) {
2511- return fn ( a , b ) ;
2512- }
25132506 // If there are any pending interactive updates, synchronously flush them.
25142507 // This needs to happen before we read any handlers, because the effect of
25152508 // the previous event may influence which handlers are called during
@@ -2523,14 +2516,13 @@ function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
25232516 performWork ( lowestPriorityPendingInteractiveExpirationTime , false ) ;
25242517 lowestPriorityPendingInteractiveExpirationTime = NoWork ;
25252518 }
2526- const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates ;
25272519 const previousIsBatchingUpdates = isBatchingUpdates ;
2528- isBatchingInteractiveUpdates = true ;
25292520 isBatchingUpdates = true ;
25302521 try {
2531- return fn ( a , b ) ;
2522+ return runWithPriority ( UserBlockingPriority , ( ) => {
2523+ return fn ( a , b ) ;
2524+ } ) ;
25322525 } finally {
2533- isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates ;
25342526 isBatchingUpdates = previousIsBatchingUpdates ;
25352527 if ( ! isBatchingUpdates && ! isRendering ) {
25362528 performSyncWork ( ) ;
@@ -2580,7 +2572,7 @@ export {
25802572 unbatchedUpdates ,
25812573 flushSync ,
25822574 flushControlled ,
2583- deferredUpdates ,
2575+ Scheduler_next as deferredUpdates ,
25842576 syncUpdates ,
25852577 interactiveUpdates ,
25862578 flushInteractiveUpdates ,
0 commit comments