React Lane 算法:一文详解 8 种 Lane 操作

本文对应的 react 版本是 18.2.0

在阅读源码时,react 有很多位操作,这些位操作大都是对 lane 的操作

如果不了解这些位操作含义,看源码时会一脸懵逼:

  1. 为什么运算前的值和运算后的值一样?
  2. 运算后这个值有什么用
  3. react 是怎么怎么判断优先级的高低

什么是 lane

lanereact@17 中用于表示任务的优先级,是对 expirationTime 的重构

lane 是一个 32 位的二进制数,每个二进制位表示 1 种优先级,优先级最高的 SyncLane1,其次为 248

lanes 是一个整数,该整数所有为二进制位为 1 对应的优先级任务都将被执行

注意:lane 的长度是 31 位,react 这么做是为了避免符号位参与运算

reactlane 的操作有 8 种:

  • lane & lane
  • lane & lanes
  • lanes & ~lane
  • lanes1 & lanes2
  • lane | lane
  • lanes2 |= lanes1 & lane
  • lane *= 2lane <<= 1
  • lanes & -lanes

在下面将会举例详细介绍这些操作,这里先介绍一下 lane 的值:

lanes 值为 0b0000000011111111111111110000000,表示有多个任务的优先级。

TransitionLane1 值为 0b0000000000000000000000010000000,表示单个任务的优先级

TransitionLane2 值为 0b0000000000000000000000100000000,表示单个任务的优先级

1. lane & lane

用来判断是不是同一个 lane,两个 lane 是否有相同的位为 1(取交集)

比如:lane & TransitionLane1,如果 lane 的值为 0b0000000000000000000000010000000,则输出 0b0000000000000000000000010000000,否则输出 0

用于 getLabelForLane 函数

2. lane & lanes

用来判断 lanes 中是否有 lane,是否有相同的位为 1(取交集)

如果想判断 lanes 中是否有 lane,进行如下计算:

TransitionLane1lanes 进行按位与,得到 lane & lanes,它的值是 0b0000000000000000000000010000000,和 TransitionLane1 值相同,说明 lanes 中有 TransitionLane1 任务

用于 isTransitionLane 等函数

3. lanes & ~lane

用来从 lanes 中删除 lane(取差集)

如果想去从 lanes 中删掉 lane,具体步骤如下:

  1. TransitionLane1 取反,得到 ~lane,即 0b1111111111111111111111101111111
  2. lanes~lane 进行按位与运算,得到 lanes & ~lane,即 0b0000000011111111111111100000000
  3. 这样就把 lanes 中的 TransitionLane1 置为了 0,也就是去掉了这个任务

用于 getNextLanes 等函数

4. lanes1 & lanes2

用于判断 lanes1 中是否有 lane 属于 lanes2(取交集)

如果想判断 lanes1 中是否有 lane 属于 lanes2,进行如下计算:

  1. 假设 lanes2SyncDefaultLanes,它是由 InputContinuousHydrationLane | InputContinuousLane |DefaultHydrationLane | DefaultLane 组成的,即 0b0000000000000000000000000111100
  2. lanes13 ~ 6 位为 1,即 lanes10b0000000000000000000000000111100
  3. lanes1 & lanes2 的值为 lanes1,即 0b0000000000000000000000000111100
  4. 说明 lanes1 中有 lanes2 中的 lane

这种用法有种变形:lanes & (lane | lane)

用于 includesNonIdleWorkincludesSyncLane 等函数

5. lane | lane

用于将多个 lane 合并为一个 lanes(取并集)

合并两个 laneTransitionLane1 | TransitionLane2,得到的值为 0b0000000000000000000000110000000

用于 markHiddenUpdate 等函数

6. lanes2 |= lanes1 & lane

用于将 lanes1 中的 lane 合并到 lanes2 中(先取交集,再取并集)

这种写法等于:lanes2 = lanes2 | (lanes1 & lane)

如果想从 lanes1 中取出 lane,并将它合并到 lanes2 中,进行如下计算:

  1. lanes1InputContinuousHydrationLane | InputContinuousLane,即 0b0000000000000000000000000001100
  2. lanes2DefaultHydrationLane | DefaultLane,即 0b0000000000000000000000000110000
  3. laneInputContinuousLane,即 0b0000000000000000000000000001000
  4. lanes1 & lane 的值为 InputContinuousLane,即 0b0000000000000000000000000001000
  5. lanes2 |= lanes1 & lane 的值为 DefaultHydrationLane | DefaultLane | InputContinuousLane,即 0b0000000000000000000000000111100
  6. lanes2 中多了 InputContinuousLane 这个任务

用于 markRootMutableReadmarkRootPinged 等函数

7. lane *= 2 和 lane <<= 1

都是将 lane 左移一位,一般来说位运算比乘法运算快

TransitionLane1 *= 2TransitionLane1 <<= 1 的结果都是 0b0000000000000000000000100000000

用于 getLaneLabelMapclaimNextRetryLane 等函数

8. lanes & -lanes

lanes 中找出最高优先级的 lane

如果想找出 lanes 中最高优先级的 lane,进行如下计算:

  1. lanes 取反,得到 ~lanes,即 0b1111111100000000000000001111111
  2. 末尾加 1,得到 -lanes,即 0b1111111100000000000000010000000
  3. lanes-lanes 进行按位与运算,得到 lanes & -lanes,即 0b0000000000000000000000010000000
  4. 这样就找出了 lanes 中最高优先级的 lane

用于 getHighestPriorityLane 函数

补充说明:

下面二进制数都是 32 位带符号位二进制数

~ 是按位取反

十进制数 4,按位取反是 -5,记做 ~4,计算逻辑如下:

  1. 将十进制数 4 转换为二进制数为 0b00000000000000000000000000000100
  2. 按位取反,即将 1 变为 0,将 0 变为 1,得到 0b11111111111111111111111111111011
  3. 符号位不变,其他位取反,得到 0b10000000000000000000000000000100
  4. 末位加 1,得到 0b10000000000000000000000000000101
  5. 将二进制数转换为十进制数,得到 -5

js 中按取反

js 中按位取反只是一个按位取反,并不表示按位取反后的数是实际的负数

  1. 十进制整数 4,转换为二进制数为 0b00000000000000000000000000000100
  2. 按位取反,即将 1 变为 0,将 0 变为 1,得到 0b11111111111111111111111111111011

4 & ~4 结果是 0

因为 4 的二进制数为 0b00000000000000000000000000000100~4 的二进制数为 0b11111111111111111111111111111011,不是 0b10000000000000000000000000000101

也就是说 ~4 的二进制数是 4 的二进制数取反

js 中的负数

十进制整数 14,负数为 -14

14 & -14 结果是 2

为什么结果会是 2 呢?

因为 14 的二进制数为 0b00000000000000000000000000001110-14 的二进制数为 0b11111111111111111111111111110010,不是 0b10000000000000000000000000001110

也就是说,-14 的二进制数是 14 的二进制数取反后再加 1

最后

  1. js 中对于二进制数操作要特别小心:~ 是按位取反(末尾不加一),- 取反末尾加一
  2. -lane === (~lane + 1)

总结

  1. lane & lane:用来判断是不是同一个 lane(是否有相同的位为 1,取交集)
  2. lane & lanes:用来判断 lanes 中是否有 lane(是否有相同的位为 1,取交集)
  3. lanes & ~lane:用来从 lanes 中删除 lane(取差集)
  4. lanes1 & lanes2:用于判断 lanes1 中是否有 lane 属于 lanes2(取交集)
  5. lane | lane:用于将多个 lane 合并为一个 lanes(取并集)
  6. lanes2 |= lanes1 & lane:用于将 lanes1 中的 lane 合并到 lanes2 中(先取交集,再取并集)
  7. lane *= 2lane <<= 1:都是将 lane 左移一位
  8. lanes & -lanes:从 lanes 中找出最高优先级的 lane

往期文章

  1. 深入探究 React 原生事件的工作原理

原文链接:https://juejin.cn/post/7214396380352266296 作者:uccs

(2)
上一篇 2023年3月25日 上午11:56
下一篇 2023年3月25日 下午7:05

相关推荐

发表回复

登录后才能评论