function shouldYield() {
if (deadlineDidExpire) {
return true;
}
// deadline.timeRemaining 返回的值表示一帧渲染时间还有剩余, 直接return false, 此时deadlineDidExpire 为false
if (
deadline === null ||
deadline.timeRemaining() > timeHeuristicForUnitOfWork
) {
// Disregard deadline.didTimeout. Only expired work should be flushed
// during a timeout. This path is only hit for non-expired work.
return false;
}
deadlineDidExpire = true;
return true;
}
function performWork(minExpirationTime: ExpirationTime, dl: Deadline | null) {
deadline = dl;
// Keep working on roots until there's no more work, or until we reach
// the deadline.
findHighestPriorityRoot();
// 异步
if (deadline !== null) {
// 重新计算当前渲染时间
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
(minExpirationTime === NoWork ||
minExpirationTime >= nextFlushedExpirationTime) &&
// 还有剩余时间更新 或者 任务已经过期需要强制输出
(!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
) {
performWorkOnRoot(
nextFlushedRoot,
nextFlushedExpirationTime,
// 过期为 true,未过期为 false
currentRendererTime >= nextFlushedExpirationTime,
);
findHighestPriorityRoot();
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;
}
} else {
// 同步
while (
// root 非空
nextFlushedRoot !== null &&
// root 有更新
nextFlushedExpirationTime !== NoWork &&
// minExpirationTime 为 Sync,那么就只能是处理 nextFlushedExpirationTime 为Sync的情况,即同步执行的情况
(minExpirationTime === NoWork ||
minExpirationTime >= nextFlushedExpirationTime)
) {
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true);
findHighestPriorityRoot();
}
}
// We're done flushing work. Either we ran out of time in this callback,
// or there's no more work left with sufficient priority.
// If we're inside a callback, set this to false since we just completed it.
if (deadline !== null) {
callbackExpirationTime = NoWork;
callbackID = null;
}
// If there's work left over, schedule a new callback.
if (nextFlushedExpirationTime !== NoWork) {
scheduleCallbackWithExpirationTime(
((nextFlushedRoot: any): FiberRoot),
nextFlushedExpirationTime,
);
}
// Clean-up.
deadline = null;
deadlineDidExpire = false;
finishRendering();
}
function performWorkOnRoot(
root: FiberRoot,
expirationTime: ExpirationTime,
isExpired: boolean,
) {
isRendering = true;
// Check if this is async work or sync/expired work.
// 同步任务 或者 过期任务
if (deadline === null || isExpired) {
// Flush work without yielding.
// TODO: Non-yieldy work does not necessarily imply expired work. A renderer
// may want to perform some work without yielding, but also without
// requiring the root to complete (by triggering placeholders).
let finishedWork = root.finishedWork;
// 上一次 renderRoot 完,但没有时间 completeRoot 了
if (finishedWork !== null) {
// This root is already complete. We can commit it.
completeRoot(root, finishedWork, expirationTime);
} else {
root.finishedWork = null;
// If this root previously suspended, clear its existing timeout, since
// we're about to try rendering again.
// suspense 功能相关
const timeoutHandle = root.timeoutHandle;
if (timeoutHandle !== noTimeout) {
root.timeoutHandle = noTimeout;
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
cancelTimeout(timeoutHandle);
}
// 表示这个任务不可中断(同步或过期任务不可中断)
const isYieldy = false;
// renderRoot 之后赋值 finishedWork
renderRoot(root, isYieldy, isExpired);
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// We've completed the root. Commit it.
completeRoot(root, finishedWork, expirationTime);
}
}
} else {
// Flush async work.
let finishedWork = root.finishedWork;
if (finishedWork !== null) {
// This root is already complete. We can commit it.
completeRoot(root, finishedWork, expirationTime);
} else {
root.finishedWork = null;
// If this root previously suspended, clear its existing timeout, since
// we're about to try rendering again.
const timeoutHandle = root.timeoutHandle;
if (timeoutHandle !== noTimeout) {
root.timeoutHandle = noTimeout;
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
cancelTimeout(timeoutHandle);
}
// 异步任务是可以中断的
const isYieldy = true;
// renderRoot 之后赋值 finishedWork
renderRoot(root, isYieldy, isExpired);
finishedWork = root.finishedWork;
// 可能被中断,被中断 finishedWork 为null
if (finishedWork !== null) {
// We've completed the root. Check the deadline one more time
// before committing.
// 还有剩余时间更新则继续 completeRoot
if (!shouldYield()) {
// Still time left. Commit the root.
completeRoot(root, finishedWork, expirationTime);
} else {
// 没有时间更新了, 待到下个时间段进入 performWorkOnRoot 判断
// There's no time left. Mark this root as complete. We'll come
// back and commit it later.
root.finishedWork = finishedWork;
}
}
}
}
isRendering = false;
}
performSyncWork 和 perfromAsyncWork
performWork
minExpirationTime
为NoWork 0
,通过判断dl.didTimeout
是否过期,如果过期则设置root.newExpirationTimeToWorkOn
为当前时间,表示这个任务可以直接执行,不需要判断是否超过帧时间minExpirationTime
为Sync 1
performAsyncWork
performWork
是否有
deadline
的区分异步/同步,用来判断一帧的渲染时间内还有没有时间留给 React 更新的循环渲染 Root 的条件:循环不同 root 和不同优先级任务来更新
超过时间片的处理:deadline 这一帧的时间到了把执行权交回浏览器
shouldYield 方法用来判断一帧渲染时间内是否还有剩余时间留个React更新,改变全局变量
deadlineDidExpire
,为 false 表示还有时间留给React更新findHighestPriorityRoot
nextFlushedRoot
, root对应的expirationTime
赋值给nextFlushedExpirationTime
performWorkonRoot
isRendering
置为true
,执行完后为false
renderRoot
渲染阶段completeRoot
提交(commit)阶段 -finishedWork
是已经完成renderRoot
渲染阶段的任务,只有renderRoot
后才不为null
completeRoot
提交。没有剩余时间则等到下个时间段进入performWorkOnRoot
判断