1 )概述
2 )源码
定位到 packages/react-reconciler/src/ReactFiberScheduler.js
function performAsyncWork() {
try {
if (!shouldYieldToRenderer()) {
// The callback timed out. That means at least one update has expired.
// Iterate through the root schedule. If they contain expired work, set
// the next render expiration time to the current time. This has the effect
// of flushing all expired work in a single batch, instead of flushing each
// level one at a time.
if (firstScheduledRoot !== null) {
recomputeCurrentRendererTime();
let root: FiberRoot = firstScheduledRoot;
do {
didExpireAtExpirationTime(root, currentRendererTime);
// The root schedule is circular, so this is never null.
root = (root.nextScheduledRoot: any);
} while (root !== firstScheduledRoot);
}
}
performWork(NoWork, true);
} finally {
didYield = false;
}
}
function performSyncWork() {
performWork(Sync, null);
}
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;
if (enableUserTimingAPI) {
const didExpire = nextFlushedExpirationTime < currentRendererTime;
const timeout = expirationTimeToMs(nextFlushedExpirationTime);
stopRequestCallbackTimer(didExpire, timeout);
}
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
(minExpirationTime === NoWork ||
minExpirationTime >= nextFlushedExpirationTime) &&
(!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
) {
performWorkOnRoot(
nextFlushedRoot,
nextFlushedExpirationTime,
currentRendererTime >= nextFlushedExpirationTime,
);
findHighestPriorityRoot();
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;
}
} else {
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
(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();
}
performAsyncWork
, performSyncWork
, performWork
三个方法连在一起performSyncWork
直接 调用 performWork(Sync, null);
就一行代码performAsyncWork
就相对复杂,是通过 react scheduler 调度回来的
dl
recomputeCurrentRendererTime
这个方法不涉及主要流程,跳过
didExpireaTexpirationTime
标记在root节点上的一些变量// packages/react-reconciler/src/ReactFiberPendingPriority.js
export function didExpireAtExpirationTime(
root: FiberRoot,
currentTime: ExpirationTime,
): void {
const expirationTime = root.expirationTime;
// 有任务,但任务过期
if (expirationTime !== NoWork && currentTime >= expirationTime) {
// The root has expired. Flush all work up to the current time.
root.nextExpirationTimeToWorkOn = currentTime; // 挂载 当前时间 作为 nextExpirationTimeToWorkOn 属性
}
}
performWork(NoWork, dl)
export const NoWork = 0
performWork
中
minExpirationTime: ExpirationTime, dl: Deadline | null
findHighestPriorityRoot
这个方法function findHighestPriorityRoot() {
let highestPriorityWork = NoWork;
let highestPriorityRoot = null;
// 这个if表示仍存在节点更新情况
if (lastScheduledRoot !== null) {
let previousScheduledRoot = lastScheduledRoot;
let root = firstScheduledRoot;
// 存在 root 就进行循环
while (root !== null) {
const remainingExpirationTime = root.expirationTime;
// 这个if判断表示,是没有任何更新的
if (remainingExpirationTime === NoWork) {
// This root no longer has work. Remove it from the scheduler.
// TODO: This check is redudant, but Flow is confused by the branch
// below where we set lastScheduledRoot to null, even though we break
// from the loop right after.
invariant(
previousScheduledRoot !== null && lastScheduledRoot !== null,
'Should have a previous and last root. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
// 这种情况,只有一个 root 节点
if (root === root.nextScheduledRoot) {
// This is the only root in the list.
root.nextScheduledRoot = null;
firstScheduledRoot = lastScheduledRoot = null;
break;
} else if (root === firstScheduledRoot) {
// 这时候 root 就没用了,可以删除了,获取 next
// This is the first root in the list.
const next = root.nextScheduledRoot;
firstScheduledRoot = next;
lastScheduledRoot.nextScheduledRoot = next;
root.nextScheduledRoot = null;
} else if (root === lastScheduledRoot) {
// 这个时候,root是最后一个
// This is the last root in the list.
lastScheduledRoot = previousScheduledRoot;
lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
root.nextScheduledRoot = null;
break;
} else {
// 移除 中间的 root.nextScheduledRoot 节点
previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
root.nextScheduledRoot = null;
}
root = previousScheduledRoot.nextScheduledRoot;
} else {
// 判断优先级,更新更高优先级
if (
highestPriorityWork === NoWork ||
remainingExpirationTime < highestPriorityWork
) {
// Update the priority, if it's higher
highestPriorityWork = remainingExpirationTime;
highestPriorityRoot = root;
}
if (root === lastScheduledRoot) {
break;
}
if (highestPriorityWork === Sync) {
// Sync is highest priority by definition so
// we can stop searching.
break;
}
previousScheduledRoot = root;
root = root.nextScheduledRoot;
}
}
}
// 处理更新后的 highestPriorityRoot 和 highestPriorityWork
nextFlushedRoot = highestPriorityRoot;
nextFlushedExpirationTime = highestPriorityWork;
}
(!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
function performWorkOnRoot(
root: FiberRoot,
expirationTime: ExpirationTime,
isExpired: boolean, // 是否过期,这个方法里一定有针对过期任务的强制更新
) {
invariant(
!isRendering,
'performWorkOnRoot was called recursively. This error is likely caused ' +
'by a bug in React. Please file an issue.',
);
isRendering = true; // 注意这里和函数结束
// Check if this is async work or sync/expired work.
// deadline === null 是 Sync的情况,isExpired 是过期的情况
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;
if (finishedWork !== null) {
// This root is already complete. We can commit it.
completeRoot(root, finishedWork, expirationTime); // 有 finishedWork 直接 completeRoot
} 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;
// 处理 timeoutHandle 的情况,跳过
if (timeoutHandle !== noTimeout) {
root.timeoutHandle = noTimeout;
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
cancelTimeout(timeoutHandle);
}
// 在这个条件下,不可中断 因为在上层if框架下,要么是 Sync的任务,要么是 过期的任务需要立即执行
const isYieldy = false;
renderRoot(root, isYieldy, isExpired);
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// We've completed the root. Commit it.
completeRoot(root, finishedWork, expirationTime);
}
}
} else {
// 这里匹配 Async 异步任务,和上面Sync的任务流程基本差不多
// Flush async work.
let finishedWork = root.finishedWork;
// 一开始进来判断这个 finishedWork,有可能在上一个时间片中,renderRoot执行完了,但是,没有时间去执行 completeRoot 了
// 需要再下次异步调度的时候进来,如果有 finishedWork 则先 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.
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(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 {
// There's no time left. Mark this root as complete. We'll come
// back and commit it later.
// 需要跳出,则赋值 finishedWork,注意这里不执行 completeRoot,因为没有时间了,需要等待下个时间片进来才能执行
root.finishedWork = finishedWork;
}
}
}
}
isRendering = false;
}
findHighestPriorityRoot
currentSchedulerTime = currentRendererTime
while(nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime))
performWorkOnRoot
和 findHighestPriorityRoot
执行掉 Sync的任务