-
Notifications
You must be signed in to change notification settings - Fork 46.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Wait for Hooks before merging] Scheduler.unstable_next #14756
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,8 +15,18 @@ import type {Interaction} from 'scheduler/src/Tracing'; | |
import { | ||
__interactionsRef, | ||
__subscriberRef, | ||
unstable_wrap as Schedule_tracing_wrap, | ||
unstable_wrap as Scheduler_tracing_wrap, | ||
} from 'scheduler/tracing'; | ||
import { | ||
unstable_next as Scheduler_next, | ||
unstable_getCurrentPriorityLevel as getCurrentPriorityLevel, | ||
unstable_runWithPriority as runWithPriority, | ||
unstable_ImmediatePriority as ImmediatePriority, | ||
unstable_UserBlockingPriority as UserBlockingPriority, | ||
unstable_NormalPriority as NormalPriority, | ||
unstable_LowPriority as LowPriority, | ||
unstable_IdlePriority as IdlePriority, | ||
} from 'scheduler'; | ||
import { | ||
invokeGuardedCallback, | ||
hasCaughtError, | ||
|
@@ -122,7 +132,7 @@ import { | |
computeAsyncExpiration, | ||
computeInteractiveExpiration, | ||
} from './ReactFiberExpirationTime'; | ||
import {ConcurrentMode, ProfileMode} from './ReactTypeOfMode'; | ||
import {ConcurrentMode, ProfileMode, NoContext} from './ReactTypeOfMode'; | ||
import {enqueueUpdate, resetCurrentlyProcessingQueue} from './ReactUpdateQueue'; | ||
import {createCapturedValue} from './ReactCapturedValue'; | ||
import { | ||
|
@@ -242,11 +252,6 @@ if (__DEV__) { | |
// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing. | ||
let lastUniqueAsyncExpiration: number = Sync - 1; | ||
|
||
// Represents the expiration time that incoming updates should use. (If this | ||
// is NoWork, use the default strategy: async updates in async mode, sync | ||
// updates in sync mode.) | ||
let expirationContext: ExpirationTime = NoWork; | ||
|
||
let isWorking: boolean = false; | ||
|
||
// The next work in progress fiber that we're currently working on. | ||
|
@@ -793,9 +798,11 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void { | |
// TODO: Avoid this extra callback by mutating the tracing ref directly, | ||
// like we do at the beginning of commitRoot. I've opted not to do that | ||
// here because that code is still in flux. | ||
callback = Schedule_tracing_wrap(callback); | ||
callback = Scheduler_tracing_wrap(callback); | ||
} | ||
passiveEffectCallbackHandle = schedulePassiveEffects(callback); | ||
passiveEffectCallbackHandle = runWithPriority(NormalPriority, () => { | ||
return schedulePassiveEffects(callback); | ||
}); | ||
passiveEffectCallback = callback; | ||
} | ||
|
||
|
@@ -1579,52 +1586,58 @@ function computeUniqueAsyncExpiration(): ExpirationTime { | |
} | ||
|
||
function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) { | ||
const priorityLevel = getCurrentPriorityLevel(); | ||
|
||
let expirationTime; | ||
if (expirationContext !== NoWork) { | ||
// An explicit expiration context was set; | ||
expirationTime = expirationContext; | ||
} else if (isWorking) { | ||
if (isCommitting) { | ||
// Updates that occur during the commit phase should have sync priority | ||
// by default. | ||
expirationTime = Sync; | ||
} else { | ||
// Updates during the render phase should expire at the same time as | ||
// the work that is being rendered. | ||
expirationTime = nextRenderExpirationTime; | ||
} | ||
if ((fiber.mode & ConcurrentMode) === NoContext) { | ||
// Outside of concurrent mode, updates are always synchronous. | ||
expirationTime = Sync; | ||
} else if (isWorking && !isCommitting) { | ||
// During render phase, updates expire during as the current render. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "during the current render"? "as the current render"? "with the current render"? |
||
expirationTime = nextRenderExpirationTime; | ||
} else { | ||
// No explicit expiration context was set, and we're not currently | ||
// performing work. Calculate a new expiration time. | ||
if (fiber.mode & ConcurrentMode) { | ||
if (isBatchingInteractiveUpdates) { | ||
// This is an interactive update | ||
switch (priorityLevel) { | ||
case ImmediatePriority: | ||
expirationTime = Sync; | ||
break; | ||
case UserBlockingPriority: | ||
expirationTime = computeInteractiveExpiration(currentTime); | ||
} else { | ||
// This is an async update | ||
break; | ||
case NormalPriority: | ||
// This is a normal, concurrent update | ||
expirationTime = computeAsyncExpiration(currentTime); | ||
} | ||
// If we're in the middle of rendering a tree, do not update at the same | ||
// expiration time that is already rendering. | ||
if (nextRoot !== null && expirationTime === nextRenderExpirationTime) { | ||
expirationTime -= 1; | ||
} | ||
} else { | ||
// This is a sync update | ||
expirationTime = Sync; | ||
break; | ||
case LowPriority: | ||
case IdlePriority: | ||
expirationTime = Never; | ||
break; | ||
default: | ||
invariant( | ||
false, | ||
'Unknown priority level. This error is likely caused by a bug in ' + | ||
'React. Please file an issue.', | ||
); | ||
} | ||
} | ||
if (isBatchingInteractiveUpdates) { | ||
// This is an interactive update. Keep track of the lowest pending | ||
// interactive expiration time. This allows us to synchronously flush | ||
// all interactive updates when needed. | ||
if ( | ||
lowestPriorityPendingInteractiveExpirationTime === NoWork || | ||
expirationTime < lowestPriorityPendingInteractiveExpirationTime | ||
) { | ||
lowestPriorityPendingInteractiveExpirationTime = expirationTime; | ||
|
||
// If we're in the middle of rendering a tree, do not update at the same | ||
// expiration time that is already rendering. | ||
if (nextRoot !== null && expirationTime === nextRenderExpirationTime) { | ||
expirationTime -= 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thing that confused me when I was reading this: is this before or after the |
||
} | ||
} | ||
|
||
// Keep track of the lowest pending interactive expiration time. This | ||
// allows us to synchronously flush all interactive updates | ||
// when needed. | ||
// TODO: Move this to renderer? | ||
if ( | ||
priorityLevel === UserBlockingPriority && | ||
(lowestPriorityPendingInteractiveExpirationTime === NoWork || | ||
expirationTime < lowestPriorityPendingInteractiveExpirationTime) | ||
) { | ||
lowestPriorityPendingInteractiveExpirationTime = expirationTime; | ||
} | ||
|
||
return expirationTime; | ||
} | ||
|
||
|
@@ -1862,34 +1875,16 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) { | |
} | ||
} | ||
|
||
function deferredUpdates<A>(fn: () => A): A { | ||
const currentTime = requestCurrentTime(); | ||
const previousExpirationContext = expirationContext; | ||
const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates; | ||
expirationContext = computeAsyncExpiration(currentTime); | ||
isBatchingInteractiveUpdates = false; | ||
try { | ||
return fn(); | ||
} finally { | ||
expirationContext = previousExpirationContext; | ||
isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates; | ||
} | ||
} | ||
|
||
function syncUpdates<A, B, C0, D, R>( | ||
fn: (A, B, C0, D) => R, | ||
a: A, | ||
b: B, | ||
c: C0, | ||
d: D, | ||
): R { | ||
const previousExpirationContext = expirationContext; | ||
expirationContext = Sync; | ||
try { | ||
return runWithPriority(ImmediatePriority, () => { | ||
return fn(a, b, c, d); | ||
} finally { | ||
expirationContext = previousExpirationContext; | ||
} | ||
}); | ||
} | ||
|
||
// TODO: Everything below this is written as if it has been lifted to the | ||
|
@@ -1910,7 +1905,6 @@ let unhandledError: mixed | null = null; | |
|
||
let isBatchingUpdates: boolean = false; | ||
let isUnbatchingUpdates: boolean = false; | ||
let isBatchingInteractiveUpdates: boolean = false; | ||
|
||
let completedBatches: Array<Batch> | null = null; | ||
|
||
|
@@ -2441,7 +2435,9 @@ function completeRoot( | |
lastCommittedRootDuringThisBatch = root; | ||
nestedUpdateCount = 0; | ||
} | ||
commitRoot(root, finishedWork); | ||
runWithPriority(ImmediatePriority, () => { | ||
commitRoot(root, finishedWork); | ||
}); | ||
} | ||
|
||
function onUncaughtError(error: mixed) { | ||
|
@@ -2507,9 +2503,6 @@ function flushSync<A, R>(fn: (a: A) => R, a: A): R { | |
} | ||
|
||
function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R { | ||
if (isBatchingInteractiveUpdates) { | ||
return fn(a, b); | ||
} | ||
// If there are any pending interactive updates, synchronously flush them. | ||
// This needs to happen before we read any handlers, because the effect of | ||
// 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 { | |
performWork(lowestPriorityPendingInteractiveExpirationTime, false); | ||
lowestPriorityPendingInteractiveExpirationTime = NoWork; | ||
} | ||
const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates; | ||
const previousIsBatchingUpdates = isBatchingUpdates; | ||
isBatchingInteractiveUpdates = true; | ||
isBatchingUpdates = true; | ||
try { | ||
return fn(a, b); | ||
return runWithPriority(UserBlockingPriority, () => { | ||
return fn(a, b); | ||
}); | ||
} finally { | ||
isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates; | ||
isBatchingUpdates = previousIsBatchingUpdates; | ||
if (!isBatchingUpdates && !isRendering) { | ||
performSyncWork(); | ||
|
@@ -2580,7 +2572,7 @@ export { | |
unbatchedUpdates, | ||
flushSync, | ||
flushControlled, | ||
deferredUpdates, | ||
Scheduler_next as deferredUpdates, | ||
syncUpdates, | ||
interactiveUpdates, | ||
flushInteractiveUpdates, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I was reading this code last week I was wondering how you'd be able to mess with the
schedulePassiveEffects
priority. Doesn't seem normally possible... unless you cause aSync
update I guess...