成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從框架作者角度聊:React調度算法的迭代過程

開發 前端 算法
在render過程中,如果又觸發交互流程,步驟2又選出一個更高優先級,則之前的render中斷,以新的優先級重新開始render。

大家好,我卡頌。

React內部最難理解的地方就是「調度算法」,不僅抽象、復雜,還重構了一次。

可以說,只有React團隊自己才能完全理解這套算法。

既然這樣,那本文嘗試從React團隊成員的視角出發,來聊聊「調度算法」。

什么是調度算法

React在v16之前面對的主要性能問題是:當組件樹很龐大時,更新狀態可能造成頁面卡頓,根本原因在于:更新流程是「同步、不可中斷的」。

為了解決這個問題,React提出Fiber架構,意在「將更新流程變為異步、可中斷的」。

最終實現的交互流程如下:

  1. 不同交互產生不同優先級的更新(比如onClick回調中的更新優先級最高,useEffect回調中觸發的更新優先級一般)
  2. 「調度算法」從眾多更新中選出一個優先級作為本次render的優先級
  3. 以步驟2選擇的優先級對組件樹進行render

在render過程中,如果又觸發交互流程,步驟2又選出一個更高優先級,則之前的render中斷,以新的優先級重新開始render。

本文要聊的就是步驟2中的「調度算法」。

expirationTime調度算法

「調度算法」需要解決的最基本的問題是:如何從眾多更新中選擇其中一個更新的優先級作為本次render的優先級?

最早的算法叫做expirationTime算法。

具體來說,更新的優先級與「觸發交互的當前時間」及「優先級對應的延遲時間」相關:

  1. // MAX_SIGNED_31_BIT_INT為最大31 bit Interger 
  2. update.expirationTime = MAX_SIGNED_31_BIT_INT - (currentTime + updatePriority); 

例如,高優先級更新u1、低優先級更新u2的updatePriority分別為0、200,則

  1. MAX_SIGNED_31_BIT_INT - (currentTime + 0) > MAX_SIGNED_31_BIT_INT - (currentTime + 200) 
  2.  
  3. // 即 
  4. u1.expirationTime > u2.expirationTime; 

代表u1優先級更高。

expirationTime算法的原理簡單易懂:每次都選出所有更新中「優先級最高的」。

如何表示“批次”

除此之外,還有個問題需要解決:如何表示「批次」?

「批次」是什么?考慮如下例子:

  1. // 定義狀態num 
  2. const [num, updateNum] = useState(0); 
  3.  
  4. // ...某些修改num的地方 
  5. // 修改的方式1 
  6. updateNum(3); 
  7. // 修改的方式2 
  8. updateNum(num => num + 1); 

兩種「修改狀態的方式」都會創建更新,區別在于:

  • 第一種方式,不需考慮更新前的狀態,直接將狀態num修改為3
  • 第二種方式,需要基于「更新前的狀態」計算新狀態

由于第二種方式的存在,更新之間可能有連續性。

所以「調度算法」計算出一個優先級后,組件render時實際參與計算「當前狀態的值」的是:

「計算出的優先級對應更新」 + 「與該優先級相關的其他優先級對應更新」

這些相互關聯,有連續性的更新被稱為一個「批次」(batch)。

expirationTime算法計算「批次」的方式也簡單粗暴:優先級大于某個值(priorityOfBatch)的更新都會劃為同一批次。

  1. const isUpdateIncludedInBatch = priorityOfUpdate >= priorityOfBatch; 

expirationTime算法保證了render異步可中斷、且永遠是最高優先級的更新先被處理。

這一時期該特性被稱為Async Mode。

IO密集型場景

Async Mode可以解決以下問題:

  1. 組件樹邏輯復雜導致更新時卡頓(因為組件render變為可中斷)
  2. 重要的交互更快響應(因為不同交互產生更新的優先級不同)

這些問題統稱為CPU密集型問題。

在前端,還有一類問題也會影響體驗,那就是「請求數據造成的等待」。這類問題被稱為IO密集型問題。

為了解決IO密集型問題的,React提出了Suspense。考慮如下代碼:

  1. const App = () => { 
  2.   const [count, setCount] = useState(0); 
  3.    
  4.   useEffect(() => { 
  5.     const t = setInterval(() => { 
  6.       setCount(count => count + 1); 
  7.     }, 1000); 
  8.     return () => clearInterval(t); 
  9.   }, []); 
  10.    
  11.   return ( 
  12.     <> 
  13.       <Suspense fallback={<div>loading...</div>}> 
  14.         <Sub count={count} /> 
  15.       </Suspense> 
  16.       <div>count is {count}</div> 
  17.     </> 
  18.   ); 
  19. }; 

其中:

  • 每過一秒會觸發一次更新,將狀態count更新為count => count + 1
  • 在Sub中會發起異步請求,請求返回前,包裹Sub的Suspense會渲染fallback

假設請求三秒后返回,理想情況下,請求發起前后UI會依次顯示為:

  1. // Sub內請求發起前 
  2. <div class=“sub”>I am sub, count is 0</div> 
  3. <div>count is 0</div> 
  4.  
  5. // Sub內請求發起第1秒 
  6. <div>loading...</div> 
  7. <div>count is 1</div> 
  8.  
  9. // Sub內請求發起第2秒 
  10. <div>loading...</div> 
  11. <div>count is 2</div> 
  12.  
  13. // Sub內請求發起第3秒 
  14. <div>loading...</div> 
  15. <div>count is 3</div> 
  16.  
  17. // Sub內請求成功后 
  18. <div class=“sub”>I am sub, request success, count is 4</div> 
  19. <div>count is 4</div> 

 從用戶的視角觀察,有兩個任務在并發執行:

  1. 請求Sub的任務(觀察第一個div的變化)
  2. 改變count的任務(觀察第二個div的變化)

Suspense帶來了「多任務并發執行」的直觀感受。

因此,Async Mode(異步模式)也更名為Concurrent Mode(并發模式)。

一個無法解決的bug

那么Suspense對應更新的優先級是高還是低呢?

當請求成功后,合理的邏輯應該是「盡快展示成功后的UI」。所以Suspense對應更新應該是高優先級更新。那么,在示例中共有兩類更新:

Suspense對應的高優IO更新,簡稱u0

每秒產生的低優CPU更新,簡稱u1、u2、u3等

在expirationTime算法下:

  1. // u0優先級遠大于u1、u2、u3... 
  2. u0.expirationTime >> u1.expirationTime > u2.expirationTime > … 

u0優先級最高,則u1及之后的更新都需要等待u0執行完畢后再進行。

而u0需要等待「請求完畢」才能執行。所以,請求發起前后UI會依次顯示為:

  1. // Sub內請求發起前 
  2. <div class=“sub”>I am sub, count is 0</div> 
  3. <div>count is 0</div> 
  4.  
  5. // Sub內請求發起第1秒 
  6. <div>loading...</div> 
  7. <div>count is 0</div> 
  8.  
  9. // Sub內請求發起第2秒 
  10. <div>loading...</div> 
  11. <div>count is 0</div> 
  12.  
  13. // Sub內請求發起第3秒 
  14. <div>loading...</div> 
  15. <div>count is 0</div> 
  16.  
  17. // Sub內請求成功后 
  18. <div class=“sub”>I am sub, request success, count is 4</div> 
  19. <div>count is 4</div> 

 從用戶的視角觀察,第二個div被卡住了3秒后突然變為4。

所以,只考慮CPU密集型場景的情況下,「高優更新先執行」的算法并無問題。

但考慮IO密集型場景的情況下,高優IO更新會阻塞低優CPU更新,這顯然是不對的。

所以expirationTime算法并不能很好支持并發更新。

expirationTime算法在線Demo[1]

出現bug的原因

expirationTime算法最大的問題在于:expirationTime字段耦合了「優先級」與「批次」這兩個概念,限制了模型的表達能力。

這導致高優IO更新不會與低優CPU更新劃為同一「批次」。那么低優CPU更新就必須等待高優IO更新處理完后再處理。

如果不同更新能根據實際情況靈活劃分「批次」,就不會產生這個bug。

重構迫在眉睫,并且重構的目標很明確:將「優先級」與「批次」拆分到兩個字段中。

Lane調度算法

新的調度算法被稱為Lane,他是如何定義「優先級」與「批次」呢?

對于優先級,一個lane就是一個32bit Interger,最高位為符號位,所以最多可以有31個位參與運算。

不同優先級對應不同lane,越低的位代表越高的優先級,比如:

  1. // 對應SyncLane,為最高優先級 
  2. 0b0000000000000000000000000000001 
  3. // 對應InputContinuousLane 
  4. 0b0000000000000000000000000000100 
  5. // 對應DefaultLane 
  6. 0b0000000000000000000000000010000 
  7. // 對應IdleLane 
  8. 0b0100000000000000000000000000000 
  9. // 對應OffscreenLane,為最低優先級 
  10. 0b1000000000000000000000000000000 

「批次」則由lanes定義,一個lanes同樣也是一個32bit Interger,代表「一到多個lane的集合」。

可以用位運算很輕松的將多個lane劃入同一個批次: 

  1. // 要使用的批次 
  2. let lanesForBatch = 0; 
  3.  
  4. const laneA = 0b0000000000000000000000001000000; 
  5. const laneB = 0b0000000000000000000000000000001; 
  6.  
  7. // 將laneA納入批次中 
  8. lanesForBatch |= laneA; 
  9. // 將laneB納入批次中 
  10. lanesForBatch |= laneB; 

上文提到的Suspense的bug是由于expirationTime算法不能靈活劃定批次導致的。

lanes就完全沒有這種顧慮,任何想劃定為同一「批次」的優先級(lane)都能用位運算輕松搞定。

Lane算法在線Demo[2]

總結

「調度算法」要解決兩個問題:

  1. 選取優先級
  2. 選取批次

expirationTime算法中使用的expirationTime字段耦合了這兩個概念,導致不夠靈活。

Lane算法的出現解決了以上問題。

參考資料

[1]expirationTime算法在線Demo:

https://codesandbox.io/s/usetransition-stop-reacting-passed-props-updates-forked-5e7lh

[2]Lane算法在線Demo:

https://codesandbox.io/s/usetransition-stop-reacting-passed-props-updates-zoqm2?file=/src/index.js

 

責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2020-06-05 07:50:04

技術思維程序員擺地攤

2019-02-18 14:42:18

React.jsUI前端

2023-11-29 07:29:28

ReactSolid

2023-09-20 23:01:03

Twitter算法

2021-07-09 09:12:40

STL排序算法

2022-12-06 08:30:06

SchedulerReact

2024-03-11 07:46:40

React優先級隊列二叉堆

2021-01-29 08:32:21

數據結構數組

2024-12-10 00:00:10

MySQLJOIN算法

2020-07-16 14:40:23

大數據計算框架

2020-05-09 14:20:11

信息安全加密

2017-05-25 12:40:06

SOA微服務系統

2015-10-16 09:59:52

SwiftCocoa

2021-12-09 08:31:01

ReentrantLoAQS

2024-09-25 08:28:45

2019-04-28 16:10:50

設計Redux前端

2025-06-03 04:00:00

Spring框架配置

2024-04-16 08:20:01

React屬性鉆取狀態管理

2011-08-08 09:32:32

Ubuntu TweaLinux

2022-10-13 10:01:12

AI模型
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美日韩精品一区二区天天拍 | 美女国产一区 | 欧美精品一二三区 | 日韩黄色免费 | 九九综合| 毛片1| 日日摸夜夜爽人人添av | 国产精品久久777777 | 午夜理伦三级理论三级在线观看 | 亚洲一区国产精品 | 女同久久另类99精品国产 | 亚洲人成人网 | 欧美一区视频 | 欧美日韩高清免费 | 国产精品久久久久久久久久久免费看 | 亚洲视频区 | 日本高清中文字幕 | 亚洲视频 欧美视频 | 亚州av| 精品欧美一区二区三区久久久 | 国产成人精品一区二区三区 | 毛片黄| 国产精品久久久久久婷婷天堂 | 亚洲国产精品一区二区第一页 | 中文字幕欧美日韩一区 | 一级大黄 | 一区二区三区国产精品 | 欧洲成人午夜免费大片 | 精品国产第一区二区三区 | a欧美| 国产一级片网站 | 一区欧美| 超碰超碰 | 尤物在线| 欧美五月婷婷 | 欧美 日韩 国产 成人 在线 91 | 国产精品久久久爽爽爽麻豆色哟哟 | 国产成人小视频 | 日韩一区二区在线观看 | 国产中文| 国产探花 |