astak16 / react-study

1 stars 0 forks source link

getNextLanes 函数是如何找到最高优先级的任务 #7

Open astak16 opened 1 year ago

astak16 commented 1 year ago

react-reconcilergetNextLanes 函数作用是:

react 一次任务调度中,找出优先级最高的任务 大概分为 4 步:

  1. 根据优先级找出优先级最高的任务
  2. 处理 wipLanesnextLanes
  3. 处理连续输入事件
  4. 处理并发过程中受影响的 lanes

其中 1/3/4 都是会改变 nextLanes2 直接 return wipLanes

astak16 commented 1 year ago

1. 根据优先级找出优先级最高的任务

  1. 首先检查是否有非闲置的 pendingLanes,如果有,说明有高优先级的更新需要处理。
  2. 然后检查非闲置的 pendingLanes 中是否有未阻塞的 lanes,如果有,说明有可以立即渲染的更新。
  3. 如果没有未阻塞的 lanes,那么检查是否有被唤醒的 lanes,如果有,说明有可以恢复渲染的更新。
  4. 如果没有非闲置的 pendingLanes,那么说明只剩下闲置的更新,这些更新可以延迟处理。
  5. 对于闲置的更新,也同样检查是否有未阻塞或被唤醒的 lanes
  6. 最后,调用 getHighestPriorityLanes 函数来获取最高优先级的 lanes,赋值给 nextLanes

1 - 3 对应的是高优先级任务

5 对应的是低优先级的任务

高优先级任务和低优先级的任务做的事情是一样的:

都是先检查有没有未阻塞的任务,再检查有没有被唤醒的任务

// 判断是否有高优先级任务
// pendingLanes & NonIdleLanes 作用是:
//  - 查看 pendingLanes 和 NonIdleLanes 之间有没有相同位为 1 的任务,如果有说明,有高优先级任务
const nonIdlePendingLanes = pendingLanes & NonIdleLanes;

// 高优先级任务
if (nonIdlePendingLanes !== NoLanes) {
  // 判断是否有被挂起的 lanes

  // nonIdlePendingLanes & ~suspendedLanes 作用是:
  //  - 将 suspendedLanes 从 nonIdlePendingLanes 中删除,如果 suspendedLanes 中有某一位为 1,那么 nonIdlePendingLanes 中的该位也会被置为 0
  //  - 如果删除了 suspendedLanes 后,nonIdlePendingLanes 仍然不为 0,说明还有阻塞的 lanes
  const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;

  // 有(未阻塞的 lanes)
  if (nonIdleUnblockedLanes !== NoLanes) {
    // 找到最高优先级的 lanes
    nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
  } else {
    // 没有(未阻塞的 lanes)

    // 判断是否有被唤醒的 lanes
    // nonIdlePendingLanes & pingedLanes 作用是:
    //  - 查看 nonIdlePendingLanes 和 pingedLanes 之间有没有相同位为 1 的任务,如果有说明,有被唤醒的 lanes
    const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
    // 有被唤醒的 lanes
    if (nonIdlePingedLanes !== NoLanes) {
      // 找到最高优先级的 lanes
      nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
    }
  }
} else {
  // 低优先级任务

  // 闲置的 pendingLanes 中 是否有未阻塞的 lanes
  const unblockedLanes = pendingLanes & ~suspendedLanes;

  // 有(未阻塞的 lanes)
  if (unblockedLanes !== NoLanes) {
    // 找到最高优先级的 lanes
    nextLanes = getHighestPriorityLanes(unblockedLanes);
  } else {
    // 没有(未阻塞的任务)

    // 闲置的 pingedLanes 中是否有被唤醒的 lanes
    // 有被环境的 lanes
    if (pingedLanes !== NoLanes) {
      // 找到最高优先级的 lanes
      nextLanes = getHighestPriorityLanes(pingedLanes);
    }
  }
}
astak16 commented 1 year ago

2. 处理 wipLanes 和 nextLanes

比较当前工作的 lanes 和下一个要工作的 lanes

  1. 如果 wipLanesnextLanes 不一致,并且没有被挂起的 lanes
  2. 如果 wipLane 优先级比 nextLane 优先级高,或者 wipLane 是过渡优先级,而 nextLane 是默认优先级
  3. 直接返回 wipLanes
// 如果当前工作的 lanes 中和 nextLanes 不一致,并且没有被挂起的 lanes
if (
  wipLanes !== NoLanes &&
  wipLanes !== nextLanes &&
  (wipLanes & suspendedLanes) === NoLanes
) {
  // 从 nextLanes 中找出优先级最高的 lane
  const nextLane = getHighestPriorityLane(nextLanes);
  // 从 wipLanes 中找出优先级最高的 lane
  const wipLane = getHighestPriorityLane(wipLanes);
  // 如果 nextLane 比 wipLane 的值要大,或者 nextLane 是默认优先级而 wipLane 是过渡优先级,将返回 wipLanes
  if (
    nextLane >= wipLane ||
    (nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes)
  ) {
    return wipLanes;
  }
}
astak16 commented 1 year ago

3. 处理连续输入事件

在同步模式下,连续输入事件的优先级会被合并一个默认优先级,以保证输入框的响应和体验

如果不合并默认优先级,那么连续输入事件的优先级可能会被其他更高的优先级打断,导致输入框的内容不同步或者丢失

// 如果当前的模式是并发模式,并且允许默认并发,那么就不改变 nextLanes
// 否则是否判断 nextLanes 中是否有 InputContinuousLane,如果有,就将 pendingLanes 中的 DefaultLane 加入到 nextLanes 中
if (
  allowConcurrentByDefault &&
  (root.current.mode & ConcurrentUpdatesByDefaultMode) !== NoMode
) {
} else if ((nextLanes & InputContinuousLane) !== NoLanes) {
  nextLanes |= pendingLanes & DefaultLane;
}
astak16 commented 1 year ago

4. 处理并发过程中受影响的 lanes

如果存在 entangledLanes,将 entangledLanes 中的 lane 也加入到 nextLanes

const entangledLanes = root.entangledLanes;
if (entangledLanes !== NoLanes) {
  const entanglements = root.entanglements;
  // nextLanes 中是否有 entangledLanes 中的 lane
  let lanes = nextLanes & entangledLanes;
  while (lanes > 0) {
    // lanes 中第一位为 1 的 index(从右往左)
    // 举例:lanes = 0b0000000000000000000000000001010,index = 3
    const index = pickArbitraryLaneIndex(lanes);
    // 左移 index 位,得到 lane
    const lane = 1 << index;
    // 从 entanglements 中取出 entangledLanes,和 nextLanes 进行或运算,在赋值给 nextLanes
    nextLanes |= entanglements[index];
    // 将 lane 从 lanes 中移除
    lanes &= ~lane;
  }
}

react 是如何知道 entangledLanesentanglements 什么位置呢?

比如现在 entangledLanes = 0b0000000000000000000000000001010

react 通过 pickArbitraryLaneIndex(lanes) 可以算出最左边的 1 的位置:

所以 entanglements 中是这么存储的 [0, 0b0000000000000000000000000000010, 0, 0b0000000000000000000000000001000]