1 )概述
2 )源码
2.1 关于 pendingTime
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L1788
function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
const root = scheduleWorkToRoot(fiber, expirationTime);
// ... 跳过很多代码
markPendingPriorityLevel(root, expirationTime); // 注意这里
// ... 跳过很多代码
}
定位到 packages/react-reconciler/src/ReactFiberPendingPriority.js#L19
进入 markPendingPriorityLevel
export function markPendingPriorityLevel(
root: FiberRoot,
expirationTime: ExpirationTime,
): void {
// If there's a gap between completing a failed root and retrying it,
// additional updates may be scheduled. Clear `didError`, in case the update
// is sufficient to fix the error.
root.didError = false;
// Update the latest and earliest pending times
const earliestPendingTime = root.earliestPendingTime;
if (earliestPendingTime === NoWork) {
// No other pending updates.
root.earliestPendingTime = root.latestPendingTime = expirationTime;
} else {
if (earliestPendingTime < expirationTime) {
// This is the earliest pending update.
root.earliestPendingTime = expirationTime;
} else {
const latestPendingTime = root.latestPendingTime;
if (latestPendingTime > expirationTime) {
// This is the latest pending update
root.latestPendingTime = expirationTime;
}
}
}
findNextExpirationTimeToWorkOn(expirationTime, root); // 该函数用于确定下一个要处理的过期时间
}
root.earliestPendingTime
earliestPendingTime === NoWork
代表现在root上面没有正在等待更新的任务
root.earliestPendingTime = root.latestPendingTime = expirationTime
earliestPendingTime < expirationTime
root.earliestPendingTime = expirationTime;
latestPendingTime > expirationTime
root.latestPendingTime = expirationTime;
findNextExpirationTimeToWorkOn
方法2.2 关于 suspendedTime
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L1433
下面是 renderRoot 中的一段代码
注意这里的 markSuspendedPriorityLevel
if (nextRenderDidError) {
// There was an error
if (hasLowerPriorityWork(root, expirationTime)) {
// There's lower priority work. If so, it may have the effect of fixing
// the exception that was just thrown. Exit without committing. This is
// similar to a suspend, but without a timeout because we're not waiting
// for a promise to resolve. React will restart at the lower
// priority level.
markSuspendedPriorityLevel(root, expirationTime); // 注意这里
// ... 跳过很多代码
return;
} else if (
// There's no lower priority work, but we're rendering asynchronously.
// Synchronsouly attempt to render the same level one more time. This is
// similar to a suspend, but without a timeout because we're not waiting
// for a promise to resolve.
!root.didError &&
isYieldy
) {
// ... 跳过很多代码
}
}
hasLowerPriorityWork
表示对于有低优先级的任务,这时候调用 markSuspendedPriorityLevel
, 进入它export function markSuspendedPriorityLevel(
root: FiberRoot,
suspendedTime: ExpirationTime,
): void {
root.didError = false;
clearPing(root, suspendedTime);
// First, check the known pending levels and update them if needed.
// 这俩值是用于记录 root 上面所有被挂起的任务,它的 expirationTime 的区间的
const earliestPendingTime = root.earliestPendingTime;
const latestPendingTime = root.latestPendingTime;
if (earliestPendingTime === suspendedTime) {
if (latestPendingTime === suspendedTime) {
// Both known pending levels were suspended. Clear them.
root.earliestPendingTime = root.latestPendingTime = NoWork;
} else {
// The earliest pending level was suspended. Clear by setting it to the
// latest pending level.
root.earliestPendingTime = latestPendingTime;
}
} else if (latestPendingTime === suspendedTime) {
// The latest pending level was suspended. Clear by setting it to the
// latest pending level.
root.latestPendingTime = earliestPendingTime; // 这里是清理的工作,后续有新的 expirationTime 进来后进行新的设置
}
// 接下去才是真正的操作
// Finally, update the known suspended levels.
const earliestSuspendedTime = root.earliestSuspendedTime;
const latestSuspendedTime = root.latestSuspendedTime;
if (earliestSuspendedTime === NoWork) {
// No other suspended levels.
root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
} else {
if (earliestSuspendedTime < suspendedTime) {
// This is the earliest suspended level.
root.earliestSuspendedTime = suspendedTime;
} else if (latestSuspendedTime > suspendedTime) {
// This is the latest suspended level
root.latestSuspendedTime = suspendedTime;
}
}
findNextExpirationTimeToWorkOn(suspendedTime, root);
}
2.3 pingedTime
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L1656
在 retrySuspendedRoot
函数内执行了 markPingedPriorityLevel
function retrySuspendedRoot(
root: FiberRoot,
boundaryFiber: Fiber,
sourceFiber: Fiber,
suspendedTime: ExpirationTime,
) {
let retryTime;
if (isPriorityLevelSuspended(root, suspendedTime)) {
// Ping at the original level
retryTime = suspendedTime;
markPingedPriorityLevel(root, retryTime); // 注意这里
} else {
// ... 跳过很多代码
}
// ... 跳过很多代码
}
retrySuspendedRoot
函数在 throw 了 Promise,并且resolve之后会调用这个方法进入 markPingedPriorityLevel
export function markPingedPriorityLevel(
root: FiberRoot,
pingedTime: ExpirationTime,
): void {
root.didError = false;
// TODO: When we add back resuming, we need to ensure the progressed work
// is thrown out and not reused during the restarted render. One way to
// invalidate the progressed work is to restart at expirationTime + 1.
const latestPingedTime = root.latestPingedTime;
if (latestPingedTime === NoWork || latestPingedTime > pingedTime) {
root.latestPingedTime = pingedTime; // 这里取最小值
}
findNextExpirationTimeToWorkOn(pingedTime, root);
}
现在发现,3种 expirationTime 的处理最终都调用了 findNextExpirationTimeToWorkOn
// packages/react-reconciler/src/ReactFiberPendingPriority.js#L248
function findNextExpirationTimeToWorkOn(completedExpirationTime, root) {
const earliestSuspendedTime = root.earliestSuspendedTime;
const latestSuspendedTime = root.latestSuspendedTime;
const earliestPendingTime = root.earliestPendingTime;
const latestPingedTime = root.latestPingedTime;
// Work on the earliest pending time. Failing that, work on the latest
// pinged time.
// 对于 nextExpirationTimeToWorkOn 的赋值,存在 earliestPendingTime 则取其值,否则取 latestPingedTime 值
let nextExpirationTimeToWorkOn =
earliestPendingTime !== NoWork ? earliestPendingTime : latestPingedTime;
// If there is no pending or pinged work, check if there's suspended work
// that's lower priority than what we just completed.
// 这时候还要判断下和 suspend 的关系
// nextExpirationTimeToWorkOn === NoWork 代表了 earliestPendingTime 和 latestPingedTime 都是 NoWork
if (
nextExpirationTimeToWorkOn === NoWork &&
(completedExpirationTime === NoWork ||
latestSuspendedTime < completedExpirationTime)
) {
// The lowest priority suspended work is the work most likely to be
// committed next. Let's start rendering it again, so that if it times out,
// it's ready to commit.
nextExpirationTimeToWorkOn = latestSuspendedTime; // 设置优先级最高的
}
// 接下去是去设置 expirationTime
let expirationTime = nextExpirationTimeToWorkOn;
if (expirationTime !== NoWork && earliestSuspendedTime > expirationTime) {
// Expire using the earliest known expiration time.
expirationTime = earliestSuspendedTime;
}
root.nextExpirationTimeToWorkOn = nextExpirationTimeToWorkOn;
root.expirationTime = expirationTime;
}
root.expirationTime = Sync
来发起一个同步的更新任务