React Lane 算法:一文详解 8 种 Lane 操作
本文对应的 react 版本是 18.2.0
在阅读源码时,react 有很多位操作,这些位操作大都是对 lane 的操作
如果不了解这些位操作含义,看源码时会一脸懵逼:
- 为什么运算前的值和运算后的值一样?
- 运算后这个值有什么用
react是怎么怎么判断优先级的高低
什么是 lane
lane 是 react@17 中用于表示任务的优先级,是对 expirationTime 的重构
lane 是一个 32 位的二进制数,每个二进制位表示 1 种优先级,优先级最高的 SyncLane 为 1,其次为 2、4、8 等
lanes 是一个整数,该整数所有为二进制位为 1 对应的优先级任务都将被执行
注意:
lane的长度是31位,react这么做是为了避免符号位参与运算
react 对 lane 的操作有 8 种:
lane & lanelane & laneslanes & ~lanelanes1 & lanes2lane | lanelanes2 |= lanes1 & lanelane *= 2和lane <<= 1lanes & -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,进行如下计算:
将 TransitionLane1 和 lanes 进行按位与,得到 lane & lanes,它的值是 0b0000000000000000000000010000000,和 TransitionLane1 值相同,说明 lanes 中有 TransitionLane1 任务
用于 isTransitionLane 等函数
3. lanes & ~lane
用来从 lanes 中删除 lane(取差集)
如果想去从 lanes 中删掉 lane,具体步骤如下:
- 对
TransitionLane1取反,得到~lane,即0b1111111111111111111111101111111 - 对
lanes和~lane进行按位与运算,得到lanes & ~lane,即0b0000000011111111111111100000000 - 这样就把
lanes中的TransitionLane1置为了0,也就是去掉了这个任务
用于 getNextLanes 等函数
4. lanes1 & lanes2
用于判断 lanes1 中是否有 lane 属于 lanes2(取交集)
如果想判断 lanes1 中是否有 lane 属于 lanes2,进行如下计算:
- 假设
lanes2为SyncDefaultLanes,它是由InputContinuousHydrationLane | InputContinuousLane |DefaultHydrationLane | DefaultLane组成的,即0b0000000000000000000000000111100 - 当
lanes1的3 ~ 6位为1,即lanes1为0b0000000000000000000000000111100 - 则
lanes1 & lanes2的值为lanes1,即0b0000000000000000000000000111100 - 说明
lanes1中有lanes2中的lane
这种用法有种变形:lanes & (lane | lane)
用于 includesNonIdleWork、includesSyncLane 等函数
5. lane | lane
用于将多个 lane 合并为一个 lanes(取并集)
合并两个 lane,TransitionLane1 | TransitionLane2,得到的值为 0b0000000000000000000000110000000
用于 markHiddenUpdate 等函数
6. lanes2 |= lanes1 & lane
用于将 lanes1 中的 lane 合并到 lanes2 中(先取交集,再取并集)
这种写法等于:lanes2 = lanes2 | (lanes1 & lane)
如果想从 lanes1 中取出 lane,并将它合并到 lanes2 中,进行如下计算:
lanes1为InputContinuousHydrationLane | InputContinuousLane,即0b0000000000000000000000000001100lanes2为DefaultHydrationLane | DefaultLane,即0b0000000000000000000000000110000lane为InputContinuousLane,即0b0000000000000000000000000001000lanes1 & lane的值为InputContinuousLane,即0b0000000000000000000000000001000lanes2 |= lanes1 & lane的值为DefaultHydrationLane | DefaultLane | InputContinuousLane,即0b0000000000000000000000000111100lanes2中多了InputContinuousLane这个任务
用于 markRootMutableRead、markRootPinged 等函数
7. lane *= 2 和 lane <<= 1
都是将 lane 左移一位,一般来说位运算比乘法运算快
TransitionLane1 *= 2 和 TransitionLane1 <<= 1 的结果都是 0b0000000000000000000000100000000
用于 getLaneLabelMap、claimNextRetryLane 等函数
8. lanes & -lanes
从 lanes 中找出最高优先级的 lane
如果想找出 lanes 中最高优先级的 lane,进行如下计算:
- 对
lanes取反,得到~lanes,即0b1111111100000000000000001111111 - 末尾加
1,得到-lanes,即0b1111111100000000000000010000000 - 对
lanes和-lanes进行按位与运算,得到lanes & -lanes,即0b0000000000000000000000010000000 - 这样就找出了
lanes中最高优先级的lane
用于 getHighestPriorityLane 函数
补充说明:
下面二进制数都是 32 位带符号位二进制数
~ 是按位取反
十进制数 4,按位取反是 -5,记做 ~4,计算逻辑如下:
- 将十进制数
4转换为二进制数为0b00000000000000000000000000000100 - 按位取反,即将
1变为0,将0变为1,得到0b11111111111111111111111111111011。 - 符号位不变,其他位取反,得到
0b10000000000000000000000000000100 - 末位加
1,得到0b10000000000000000000000000000101 - 将二进制数转换为十进制数,得到
-5。
js 中按取反
js 中按位取反只是一个按位取反,并不表示按位取反后的数是实际的负数
- 十进制整数
4,转换为二进制数为0b00000000000000000000000000000100 - 按位取反,即将
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
最后
- 在
js中对于二进制数操作要特别小心:~是按位取反(末尾不加一),-取反末尾加一 -lane === (~lane + 1)
总结
lane & lane:用来判断是不是同一个lane(是否有相同的位为1,取交集)lane & lanes:用来判断lanes中是否有lane(是否有相同的位为1,取交集)lanes & ~lane:用来从lanes中删除lane(取差集)lanes1 & lanes2:用于判断lanes1中是否有lane属于lanes2(取交集)lane | lane:用于将多个lane合并为一个lanes(取并集)lanes2 |= lanes1 & lane:用于将lanes1中的lane合并到lanes2中(先取交集,再取并集)lane *= 2和lane <<= 1:都是将lane左移一位lanes & -lanes:从lanes中找出最高优先级的lane
往期文章
更多 react 源码文章
转载自:https://juejin.cn/post/7214396380352266296