<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近在重學React,由於近兩年沒使用React突然重學發現一些很有意思的概念,首先便是React的Scheduler(排程器) 由於我對React的概念還停留在React 15之前(就是那個沒有hooks的年代),所以接觸Scheduler(排程器) 讓我感覺很有意思;
在我印象中React的架構分為兩層(React 16 之前)
如今增加了Scheduler(排程器) ,那麼排程器有什麼用?排程器的作用是排程任務的優先順序,高優任務優先進入Reconciler
要了解為什麼需要Scheduler(排程器) 我們需要知道以下幾個痛點;
首先我們講講React何時進行更新,眾所周知主流的瀏覽器的重新整理頻率是60HZ,也就是說主流的瀏覽器完成一次重新整理需要1000/60 ms約等於16.666ms
然後我們需要知道瀏覽器在你開啟一個頁面的時候做了什麼;總結下來就是一張圖
CSSOM樹的構建時機與JS的執行時機是依據你解析的link標籤與script標籤來確認的;因為當React開始更新時已完成部分工作(開始迴流與重繪),所以經過精簡,可以歸為以下幾個步驟
而以上的整個過程稱之為一幀,通俗點講就是在16.6ms之內(主流瀏覽器)js的事件迴圈進行完成之後會對頁面進行渲染;那麼React在何時對頁面進行更新呢?react會在執行完以上整個過程之後的空閒時間進行更新,所以如果執行以上流程用了10ms則react會在餘下的6.6ms內進行更新(一般5ms左右);
在React16之前元件的mount階段會呼叫mountComponent,update階段會呼叫updateComponent,我們知道react的更新是從外向內進行更新,所以當時的做法是使用遞迴逐步更新子元件,而這個過程是不可中斷的,所以當子元件巢狀層級過深則會出現卡頓,因為這個過程是同步不可中斷的,所以react16之前採用的是同步更新策略,這顯然不符合React的快速響應理念;
為了解決以上同步更新所帶來的痛點,React16採用了非同步可中斷更新來替代它,所以在React16當中引入了Scheduler(排程器)
Scheduler主要包含兩個作用
關於時間切片很好理解,我們已經提到了Readt的更新會在重繪呈現之後的空閒時間執行;所以在本質上與requestIdleCallback 這個方法很相似;
requestIdleCallback(fn,timeout)
這個方法常用於處理一些優先順序比較低的任務,任務會在瀏覽器空閒的時候執行而它有兩個致命缺陷
因此React放棄了requestIdleCallback 而實現了功能更加強大的requestIdleCallback polyfill 也就是 Scheduler
首先我們看下JS在瀏覽器中的執行流程與requestIdleCallback的執行時機
而Scheduler的時間切片將以回撥函數的方式在非同步宏任務當中執行;請看原始碼
var schedulePerformWorkUntilDeadline; //node與舊版IE中執行 if (typeof localSetImmediate === 'function') { // Node.js and old IE. // There's a few reasons for why we prefer setImmediate. // // Unlike MessageChannel, it doesn't prevent a Node.js process from exiting. // (Even though this is a DOM fork of the Scheduler, you could get here // with a mix of Node.js 15+, which has a MessageChannel, and jsdom.) // https://github.com/facebook/react/issues/20756 // // But also, it runs earlier which is the semantic we want. // If other browsers ever implement it, it's better to use it. // Although both of these would be inferior to native scheduling. schedulePerformWorkUntilDeadline = function () { localSetImmediate(performWorkUntilDeadline); }; } else if (typeof MessageChannel !== 'undefined') { //判斷瀏覽器能否執行MessageChannel物件,同屬非同步宏任務,優先順序高於setTimeout // DOM and Worker environments. // We prefer MessageChannel because of the 4ms setTimeout clamping. var channel = new MessageChannel(); var port = channel.port2; channel.port1.onmessage = performWorkUntilDeadline; schedulePerformWorkUntilDeadline = function () { port.postMessage(null); }; } else { //如果當前非舊IE與node環境並且不具備MessageChannel則使用setTimeout執行回撥函數 // We should only fallback here in non-browser environments. schedulePerformWorkUntilDeadline = function () { localSetTimeout(performWorkUntilDeadline, 0); }; }
可以看到Scheduler在使用了三種非同步宏任務方式,在舊版IE與node環境中使用setImmediate,在一般情況下使用MessageChannel如果當前環境不支援MessageChannel則改用setTimeout
那麼講完時間切片,我們來講講排程優先順序;首先我們要知道對應的五種優先順序
// Times out immediately var IMMEDIATE_PRIORITY_TIMEOUT = -1;//已經過期 // Eventually times out var USER_BLOCKING_PRIORITY_TIMEOUT = 250;//將要過期 var NORMAL_PRIORITY_TIMEOUT = 5000;//一般優先順序任務 var LOW_PRIORITY_TIMEOUT = 10000;//低優先順序任務 // Never times out var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;//最低優先順序
可以看到過期時長越低的任務優先順序越高,Scheduler是根據任務優先順序情況來排程的,它會優先排程優先順序高的任務,再排程優先順序低的任務,如果在排程低優先順序任務時突然插入一個高優先順序任務則會中斷並儲存該任務讓高優先順序任務插隊,在之後有空閒時間片再從佇列中取出執行;我們來看主入口函數unstable_scheduleCallback
function unstable_scheduleCallback(priorityLevel, callback, options) { var currentTime = exports.unstable_now(); var startTime; //獲取任務延遲 if (typeof options === 'object' && options !== null) { var delay = options.delay; if (typeof delay === 'number' && delay > 0) { //延遲任務 startTime = currentTime + delay; } else { startTime = currentTime; } } else { startTime = currentTime; } var timeout; //根據不同優先順序對應時間給timeout賦值(過期時間) switch (priorityLevel) { case ImmediatePriority: timeout = IMMEDIATE_PRIORITY_TIMEOUT; break; case UserBlockingPriority: timeout = USER_BLOCKING_PRIORITY_TIMEOUT; break; case IdlePriority: timeout = IDLE_PRIORITY_TIMEOUT; break; case LowPriority: timeout = LOW_PRIORITY_TIMEOUT; break; case NormalPriority: default: timeout = NORMAL_PRIORITY_TIMEOUT; break; } //計算任務延遲時間(執行) var expirationTime = startTime + timeout; //新任務初始化 var newTask = { id: taskIdCounter++, callback: callback, priorityLevel: priorityLevel, startTime: startTime, expirationTime: expirationTime, sortIndex: -1 }; //如果startTime大於currentTime則說明優先順序低,為延遲任務 if (startTime > currentTime) { // This is a delayed task. //將startTime存入新任務,用於任務排序(執行順序) newTask.sortIndex = startTime; //採用小頂堆,將新任務插入延遲任務佇列進行排序 //當前startTime > currentTime所以當前任務為延遲任務插入延遲任務佇列 push(timerQueue, newTask); //若可執行任務佇列為空或者新任務為延遲任務的第一個 if (peek(taskQueue) === null && newTask === peek(timerQueue)) { // All tasks are delayed, and this is the task with the earliest delay. if (isHostTimeoutScheduled) { // Cancel an existing timeout. //取消延時排程 cancelHostTimeout(); } else { isHostTimeoutScheduled = true; } // Schedule a timeout. requestHostTimeout(handleTimeout, startTime - currentTime); } } else { newTask.sortIndex = expirationTime; //推入可執行佇列 push(taskQueue, newTask); // wait until the next time we yield. //當前可排程無插隊任務 if (!isHostCallbackScheduled && !isPerformingWork) { isHostCallbackScheduled = true; requestHostCallback(flushWork);//執行 } } return newTask; }
從程式碼中可以看到Scheduler中的任務以佇列的形式進行儲存分別是 可執行佇列taskQueue與延遲佇列timerQueue 當新任務進入方法unstable_scheduleCallback會將任放到延遲佇列timerQueue中進行排序(優先順序依照任務的sortIndex),如果延遲佇列timerQueue中有任務變成可執行狀態(currentTmie>startTime)則我們會將任務放入我們會將任務取出並放入可執行佇列taskQueue並取出最快到期的任務執行
React是以非同步可中斷的更新來替代原有的同步更新,而實現非同步可中斷更新的關鍵是Scheduler,Scheduler主要的功能是時間切片與優先順序排程,實現時間切片的關鍵是requestIdleCallback polyfill,排程任務為非同步宏任務。而實現優先順序排程的關鍵是當前任務到期時間,到期時間短的優先順序更高,根據任務的優先順序分別儲存在可執行佇列與延時佇列;
以上就是React為什麼需要Scheduler排程器原理詳解的詳細內容,更多關於React Scheduler排程器原理的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45