Skip to content

Commit

Permalink
Use callback priority to determine cancellation
Browse files Browse the repository at this point in the history
  • Loading branch information
rickhanlonii committed Jan 27, 2021
1 parent bf6990a commit 4fdeaa0
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 27 deletions.
64 changes: 51 additions & 13 deletions packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -712,33 +712,68 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
// This returns the priority level computed during the `getNextLanes` call.
const newCallbackPriority = returnNextLanesPriority();

if (nextLanes === NoLanes) {
// Special case: There's nothing to work on.
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
if (enableDiscreteEventMicroTasks) {
if (nextLanes === NoLanes) {
// Special case: There's nothing to work on.
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
}
root.callbackNode = null;
root.callbackPriority = NoLanePriority;
return;
}
return;
}

// Check if there's an existing task. We may be able to reuse it.
if (existingCallbackNode !== null) {
// Check if there's an existing task. We may be able to reuse it.
const existingCallbackPriority = root.callbackPriority;
if (existingCallbackPriority === newCallbackPriority) {
// The priority hasn't changed. We can reuse the existing task. Exit.
if (
existingCallbackPriority !== NoLanePriority &&
existingCallbackPriority !== InputDiscreteLanePriority
) {
if (existingCallbackPriority === newCallbackPriority) {
// The priority hasn't changed. We can reuse the existing task. Exit.
return;
}

invariant(
existingCallbackNode != null,
'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',
);

// The priority changed. Cancel the existing callback. We'll schedule a new
// one below.
cancelCallback(existingCallbackNode);
}
} else {
if (nextLanes === NoLanes) {
// Special case: There's nothing to work on.
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
root.callbackNode = null;
root.callbackPriority = NoLanePriority;
}
return;
}
// The priority changed. Cancel the existing callback. We'll schedule a new
// one below.
cancelCallback(existingCallbackNode);

// Check if there's an existing task. We may be able to reuse it.
if (existingCallbackNode !== null) {
const existingCallbackPriority = root.callbackPriority;
if (existingCallbackPriority === newCallbackPriority) {
// The priority hasn't changed. We can reuse the existing task. Exit.
return;
}
// The priority changed. Cancel the existing callback. We'll schedule a new
// one below.
cancelCallback(existingCallbackNode);
}
}

// Schedule a new callback.
let newCallbackNode;
if (newCallbackPriority === SyncLanePriority) {
// Special case: Sync React callbacks are scheduled on a special
// internal queue

// TODO: After enableDiscreteEventMicroTasks lands, we can remove the fake node.
newCallbackNode = scheduleSyncCallback(
performSyncWorkOnRoot.bind(null, root),
);
Expand Down Expand Up @@ -1879,6 +1914,9 @@ function commitRootImpl(root, renderPriorityLevel) {
// commitRoot never returns a continuation; it always finishes synchronously.
// So we can clear these now to allow a new callback to be scheduled.
root.callbackNode = null;
if (enableDiscreteEventMicroTasks) {
root.callbackPriority = NoLanePriority;
}

// Update the first and last pending times on this root. The new first
// pending time is whatever is left on the root fiber.
Expand Down
64 changes: 51 additions & 13 deletions packages/react-reconciler/src/ReactFiberWorkLoop.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -694,33 +694,68 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
// This returns the priority level computed during the `getNextLanes` call.
const newCallbackPriority = returnNextLanesPriority();

if (nextLanes === NoLanes) {
// Special case: There's nothing to work on.
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
if (enableDiscreteEventMicroTasks) {
if (nextLanes === NoLanes) {
// Special case: There's nothing to work on.
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
}
root.callbackNode = null;
root.callbackPriority = NoLanePriority;
return;
}
return;
}

// Check if there's an existing task. We may be able to reuse it.
if (existingCallbackNode !== null) {
// Check if there's an existing task. We may be able to reuse it.
const existingCallbackPriority = root.callbackPriority;
if (existingCallbackPriority === newCallbackPriority) {
// The priority hasn't changed. We can reuse the existing task. Exit.
if (
existingCallbackPriority !== NoLanePriority &&
existingCallbackPriority !== InputDiscreteLanePriority
) {
if (existingCallbackPriority === newCallbackPriority) {
// The priority hasn't changed. We can reuse the existing task. Exit.
return;
}

invariant(
existingCallbackNode != null,
'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',
);

// The priority changed. Cancel the existing callback. We'll schedule a new
// one below.
cancelCallback(existingCallbackNode);
}
} else {
if (nextLanes === NoLanes) {
// Special case: There's nothing to work on.
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
root.callbackNode = null;
root.callbackPriority = NoLanePriority;
}
return;
}
// The priority changed. Cancel the existing callback. We'll schedule a new
// one below.
cancelCallback(existingCallbackNode);

// Check if there's an existing task. We may be able to reuse it.
if (existingCallbackNode !== null) {
const existingCallbackPriority = root.callbackPriority;
if (existingCallbackPriority === newCallbackPriority) {
// The priority hasn't changed. We can reuse the existing task. Exit.
return;
}
// The priority changed. Cancel the existing callback. We'll schedule a new
// one below.
cancelCallback(existingCallbackNode);
}
}

// Schedule a new callback.
let newCallbackNode;
if (newCallbackPriority === SyncLanePriority) {
// Special case: Sync React callbacks are scheduled on a special
// internal queue

// TODO: After enableDiscreteEventMicroTasks lands, we can remove the fake node.
newCallbackNode = scheduleSyncCallback(
performSyncWorkOnRoot.bind(null, root),
);
Expand Down Expand Up @@ -1859,6 +1894,9 @@ function commitRootImpl(root, renderPriorityLevel) {
// commitRoot never returns a continuation; it always finishes synchronously.
// So we can clear these now to allow a new callback to be scheduled.
root.callbackNode = null;
if (enableDiscreteEventMicroTasks) {
root.callbackPriority = NoLanePriority;
}

// Update the first and last pending times on this root. The new first
// pending time is whatever is left on the root fiber.
Expand Down
3 changes: 2 additions & 1 deletion scripts/error-codes/codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -372,5 +372,6 @@
"381": "This feature is not supported by ReactSuspenseTestUtils.",
"382": "This query has received more parameters than the last time the same query was used. Always pass the exact number of parameters that the query needs.",
"383": "This query has received fewer parameters than the last time the same query was used. Always pass the exact number of parameters that the query needs.",
"384": "Refreshing the cache is not supported in Server Components."
"384": "Refreshing the cache is not supported in Server Components.",
"385": "Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue."
}

0 comments on commit 4fdeaa0

Please sign in to comment.