React 源码 - 异步可中断、饥饿问题

342 阅读2分钟
render()
    |
    |
    V

// 初始化应用的起点
updateContainer(element,container,parentComponent,callback)
    |
    |
    V
    
// 获取一个可用的 lane - '赛道'
requestUpdateLane(fiber) lane :例如 - 0b0000000001111111111111111000000
// 获取 scheduler 优先级
const schedulerPriority = getCurrentPriorityLevel(); 
// 将 scheduler 优先级转为 react 的 lane 优先级
const schedulerLanePriority = schedulerPriorityToLanePriority(schedulerPriority)
// 获取能用的 lane 赛道
lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
    |
    |
    V

// 生成一个update对象
var update = createUpdate(eventTime, lane);
    |
    |
    V

// fiber 内调度 update
scheduleUpdateOnFiber(root, current$1, lane, eventTime);
    |
    |
    V

// 向上找 root上 的过程中,回合并当前的 lanes 确保调度的优先级
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
markRootUpdated(root: FiberRoot, updateLane: Lane, eventTime: number,)
root.pendingLanes |= updateLane;
    |
    |
    V

// 调度根节点
ensureRootIsScheduled(root, eventTime);
var existingCallbackNode = root.callbackNode; // 调度的任务
    |
    |
    V

// 确定下一个要工作的车道,以及它们的优先级 
// 1、如果低优先级的任务一直被高优先级的任务给打断
// 2、低优先级的任务会过期,会变成同步的任务
// 3、react 通过这中让低优先级任务过期的过期的方式解决饥饿问题
markStarvedLanesAsExpired(root, currentTime); // 标记'饥饿'的lane
computeExpirationTime(lane, currentTime); // 计算过期时间
    |
    |
    V


// 返回优先级最高的 Lane, 解决饥饿问题
// 1、判断是否有正在被调度的工作 root.pendingLanes ,无直接 return;
// 2、如果存在过期的lane:root.expiredLanes 优先被调度,解决饥饿问题
// 3、如果不存在过期的lane:在 root.pendingLanes 中返回优先级最高的 lane
var nextLanes = getNextLanes(
    root, 
    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
);
// 获取对应的优先级
// 1、如果 nextLanes 不存在,没任务直接中断
// 2、如果当前存在被调度的任务 existingCallbackNode
//   如果 existingCallbackNode == newCallbackPriority 中断,
//   否则 cancelCallback(existingCallbackNode) 取消原来的任务,重新执行新任务
var existingCallbackNode = root.callbackNode;
var newCallbackPriority = returnNextLanesPriority();
// 将 lane 的优先级再转为 schedule 的优先级,因为要调用 scheduleCallback 方法
var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);
// 调用 performConcurrentWorkOnRoot 方法
newCallbackNode = scheduleCallback(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));
    |
    |
    V

performConcurrentWorkOnRoot(root, didTimeout)
// 1、当前调用的回调函数 === 已经存在的回调函数( 继续被调度)
ensureRootIsScheduled(root, now());
if (root.callbackNode === originalCallbackNode) {
    return performConcurrentWorkOnRoot.bind(null, root);
}
return null;
// 2、如果有更高优先级的任务被调度
return null;
// 3、前两个步骤结合去看 function workLoop(hasTimeRemaining, initialTime) 
function workLoopConcurrent() {
  // 中断可连续:shouldYield
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}
    |
    |
    V
commit()
......