不同类型的 expirationTime
1 )概述
- 在 react 中有几种不同类型的 expirationTime
- pendingTime
- suspendedTime
- pingedTime
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); // 注意这里
// ... 跳过很多代码
}
- 所有创建更新都会通过调用 scheduleWork 来开始进行一个任务队列的调度的一个过程
- 因为对于调用 scheduleWork 进来的任务,都认为它是 pending 的任务
- pending 的任务就是一个等待渲染的一个任务, 这个任务因为没有经过任何的更新,没有经过像 suspend 这样的流程
- 所以它是一个新创建的任务,是一个pending的任务
定位到 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 而且
earliestPendingTime < expirationTime- earliestPendingTime 表示最早的等待更新时间,取最大值,优先级最小
root.earliestPendingTime = expirationTime;
- 如果
latestPendingTime > expirationTime- latestPendingTime 表示最晚的等待更新时间,取最小值, 优先级最大
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); }- 这个方法跟设置 pendingTime 的方法其实是差不多的
- 这里不进行逐个解释, 主要功能是处理被挂起任务的 expirationTime 最早和最晚的区间的
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;
}
- 这里可以看到 expirationTime 和 nextExpirationTimeToWorkOn 的区别在于
- 如果有 earliestSuspendedTime 并且比 expirationTime 要大, 即 earliestSuspendedTime的优先级较低
- 则 expirationTime 取 earliestSuspendedTime,取优先级低的
- 还有,就是比如在 renderRoot 里面,手动设置
root.expirationTime = Sync来发起一个同步的更新任务- 这就是 expirationTime 的作用
- 如果有 earliestSuspendedTime 并且比 expirationTime 要大, 即 earliestSuspendedTime的优先级较低
- 综上,目前大致了解了在root上面有这三种不同的值来记录不同场景产生的更新中相关 time 的一个范围
410



被折叠的 条评论
为什么被折叠?



