Skip to content
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

Merged
merged 4 commits into from
Feb 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions packages/react-cache/src/__tests__/ReactCache-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ describe('ReactCache', () => {
beforeEach(() => {
jest.resetModules();

let currentPriorityLevel = 3;

jest.mock('scheduler', () => {
let callbacks = [];
return {
Expand All @@ -38,6 +40,26 @@ describe('ReactCache', () => {
callback();
}
},

unstable_ImmediatePriority: 1,
unstable_UserBlockingPriority: 2,
unstable_NormalPriority: 3,
unstable_LowPriority: 4,
unstable_IdlePriority: 5,

unstable_runWithPriority(priorityLevel, fn) {
const prevPriorityLevel = currentPriorityLevel;
currentPriorityLevel = priorityLevel;
try {
return fn();
} finally {
currentPriorityLevel = prevPriorityLevel;
}
},

unstable_getCurrentPriorityLevel() {
return currentPriorityLevel;
},
};
});

Expand Down
148 changes: 70 additions & 78 deletions packages/react-reconciler/src/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
});
Copy link
Contributor

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 a Sync update I guess...

passiveEffectCallback = callback;
}

Expand Down Expand Up @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
Copy link
Contributor

Choose a reason for hiding this comment

The 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 nextRenderExpirationTime?

}
}

// 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;
}

Expand Down Expand Up @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -2441,7 +2435,9 @@ function completeRoot(
lastCommittedRootDuringThisBatch = root;
nestedUpdateCount = 0;
}
commitRoot(root, finishedWork);
runWithPriority(ImmediatePriority, () => {
commitRoot(root, finishedWork);
});
}

function onUncaughtError(error: mixed) {
Expand Down Expand Up @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -2580,7 +2572,7 @@ export {
unbatchedUpdates,
flushSync,
flushControlled,
deferredUpdates,
Scheduler_next as deferredUpdates,
syncUpdates,
interactiveUpdates,
flushInteractiveUpdates,
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/ReactSharedInternals.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
unstable_now,
unstable_scheduleCallback,
unstable_runWithPriority,
unstable_next,
unstable_getFirstCallbackNode,
unstable_pauseExecution,
unstable_continueExecution,
Expand Down Expand Up @@ -53,6 +54,7 @@ if (__UMD__) {
unstable_now,
unstable_scheduleCallback,
unstable_runWithPriority,
unstable_next,
unstable_wrapCallback,
unstable_getFirstCallbackNode,
unstable_pauseExecution,
Expand Down
8 changes: 8 additions & 0 deletions packages/scheduler/npm/umd/scheduler.development.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
);
}

function unstable_next() {
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_next.apply(
this,
arguments
);
}

function unstable_wrapCallback() {
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_wrapCallback.apply(
this,
Expand Down Expand Up @@ -95,6 +102,7 @@
unstable_cancelCallback: unstable_cancelCallback,
unstable_shouldYield: unstable_shouldYield,
unstable_runWithPriority: unstable_runWithPriority,
unstable_next: unstable_next,
unstable_wrapCallback: unstable_wrapCallback,
unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
unstable_continueExecution: unstable_continueExecution,
Expand Down
8 changes: 8 additions & 0 deletions packages/scheduler/npm/umd/scheduler.production.min.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
);
}

function unstable_next() {
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_next.apply(
this,
arguments
);
}

function unstable_wrapCallback() {
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_wrapCallback.apply(
this,
Expand Down Expand Up @@ -89,6 +96,7 @@
unstable_cancelCallback: unstable_cancelCallback,
unstable_shouldYield: unstable_shouldYield,
unstable_runWithPriority: unstable_runWithPriority,
unstable_next: unstable_next,
unstable_wrapCallback: unstable_wrapCallback,
unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
unstable_continueExecution: unstable_continueExecution,
Expand Down
8 changes: 8 additions & 0 deletions packages/scheduler/npm/umd/scheduler.profiling.min.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
);
}

function unstable_next() {
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_next.apply(
this,
arguments
);
}

function unstable_wrapCallback() {
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_wrapCallback.apply(
this,
Expand Down Expand Up @@ -89,6 +96,7 @@
unstable_cancelCallback: unstable_cancelCallback,
unstable_shouldYield: unstable_shouldYield,
unstable_runWithPriority: unstable_runWithPriority,
unstable_next: unstable_next,
unstable_wrapCallback: unstable_wrapCallback,
unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
unstable_continueExecution: unstable_continueExecution,
Expand Down
Loading