欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React為什么需要Scheduler調(diào)度器原理詳解

 更新時間:2022年10月29日 11:40:59   作者:龍騎士尹道長  
這篇文章主要為大家介紹了React為什么需要Scheduler調(diào)度器原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

最近在重學(xué)React,由于近兩年沒使用React突然重學(xué)發(fā)現(xiàn)一些很有意思的概念,首先便是React的Scheduler(調(diào)度器) 由于我對React的概念還停留在React 15之前(就是那個沒有hooks的年代),所以接觸Scheduler(調(diào)度器) 讓我感覺很有意思;

在我印象中React的架構(gòu)分為兩層(React 16 之前)

  • Reconciler(協(xié)調(diào)器)—— 負(fù)責(zé)找出變化的組件
  • Renderer(渲染器)—— 負(fù)責(zé)將變化的組件渲染到頁面上

如今增加了Scheduler(調(diào)度器) ,那么調(diào)度器有什么用?調(diào)度器的作用是調(diào)度任務(wù)的優(yōu)先級,高優(yōu)任務(wù)優(yōu)先進入Reconciler

我們?yōu)槭裁葱枰猄cheduler(調(diào)度器)

要了解為什么需要Scheduler(調(diào)度器) 我們需要知道以下幾個痛點;

  • React在何時進行更新;
  • 16之前的React怎樣進行更新;
  • 16之前的React帶來的痛點;

首先我們講講React何時進行更新,眾所周知主流的瀏覽器的刷新頻率是60HZ,也就是說主流的瀏覽器完成一次刷新需要1000/60 ms約等于16.666ms

然后我們需要知道瀏覽器在你開啟一個頁面的時候做了什么;總結(jié)下來就是一張圖

CSSOM樹的構(gòu)建時機與JS的執(zhí)行時機是依據(jù)你解析的link標(biāo)簽與script標(biāo)簽來確認(rèn)的;因為當(dāng)React開始更新時已完成部分工作(開始回流與重繪),所以經(jīng)過精簡,可以歸為以下幾個步驟

而以上的整個過程稱之為一幀,通俗點講就是在16.6ms之內(nèi)(主流瀏覽器)js的事件循環(huán)進行完成之后會對頁面進行渲染;那么React在何時對頁面進行更新呢?react會在執(zhí)行完以上整個過程之后的空閑時間進行更新,所以如果執(zhí)行以上流程用了10ms則react會在余下的6.6ms內(nèi)進行更新(一般5ms左右);

在React16之前組件的mount階段會調(diào)用mountComponent,update階段會調(diào)用updateComponent,我們知道react的更新是從外向內(nèi)進行更新,所以當(dāng)時的做法是使用遞歸逐步更新子組件,而這個過程是不可中斷的,所以當(dāng)子組件嵌套層級過深則會出現(xiàn)卡頓,因為這個過程是同步不可中斷的,所以react16之前采用的是同步更新策略,這顯然不符合React的快速響應(yīng)理念;

為了解決以上同步更新所帶來的痛點,React16采用了異步可中斷更新來替代它,所以在React16當(dāng)中引入了Scheduler(調(diào)度器)

Scheduler如何進行工作

Scheduler主要包含兩個作用

  • 時間切片
  • 優(yōu)先級調(diào)度

關(guān)于時間切片很好理解,我們已經(jīng)提到了Readt的更新會在重繪呈現(xiàn)之后的空閑時間執(zhí)行;所以在本質(zhì)上與requestIdleCallback 這個方法很相似;

requestIdleCallback(fn,timeout)

這個方法常用于處理一些優(yōu)先級比較低的任務(wù),任務(wù)會在瀏覽器空閑的時候執(zhí)行而它有兩個致命缺陷

  • 不是所有瀏覽器適用(兼容性)
  • 觸發(fā)不穩(wěn)定,在瀏覽器FPS為20左右的時候會比較流暢(違背React快速響應(yīng))

因此React放棄了requestIdleCallback 而實現(xiàn)了功能更加強大的requestIdleCallback polyfill 也就是 Scheduler

首先我們看下JS在瀏覽器中的執(zhí)行流程與requestIdleCallback的執(zhí)行時機

Scheduler的時間切片將以回調(diào)函數(shù)的方式在異步宏任務(wù)當(dāng)中執(zhí)行;請看源碼

var schedulePerformWorkUntilDeadline;
//node與舊版IE中執(zhí)行
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') {
  //判斷瀏覽器能否執(zhí)行MessageChannel對象,同屬異步宏任務(wù),優(yōu)先級高于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 {
  //如果當(dāng)前非舊IE與node環(huán)境并且不具備MessageChannel則使用setTimeout執(zhí)行回調(diào)函數(shù)
  // We should only fallback here in non-browser environments.
  schedulePerformWorkUntilDeadline = function () {
    localSetTimeout(performWorkUntilDeadline, 0);
  };
}

可以看到Scheduler在使用了三種異步宏任務(wù)方式,在舊版IE與node環(huán)境中使用setImmediate,在一般情況下使用MessageChannel如果當(dāng)前環(huán)境不支持MessageChannel則改用setTimeout

那么講完時間切片,我們來講講調(diào)度優(yōu)先級;首先我們要知道對應(yīng)的五種優(yōu)先級

// Times out immediately
var IMMEDIATE_PRIORITY_TIMEOUT = -1;//已經(jīng)過期
// Eventually times out
var USER_BLOCKING_PRIORITY_TIMEOUT = 250;//將要過期
var NORMAL_PRIORITY_TIMEOUT = 5000;//一般優(yōu)先級任務(wù)
var LOW_PRIORITY_TIMEOUT = 10000;//低優(yōu)先級任務(wù)
// Never times out
var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;//最低優(yōu)先級

可以看到過期時長越低的任務(wù)優(yōu)先級越高,Scheduler是根據(jù)任務(wù)優(yōu)先級情況來調(diào)度的,它會優(yōu)先調(diào)度優(yōu)先級高的任務(wù),再調(diào)度優(yōu)先級低的任務(wù),如果在調(diào)度低優(yōu)先級任務(wù)時突然插入一個高優(yōu)先級任務(wù)則會中斷并保存該任務(wù)讓高優(yōu)先級任務(wù)插隊,在之后有空閑時間片再從隊列中取出執(zhí)行;我們來看主入口函數(shù)unstable_scheduleCallback

function unstable_scheduleCallback(priorityLevel, callback, options) {
  var currentTime = exports.unstable_now();
  var startTime;
   //獲取任務(wù)延遲
  if (typeof options === 'object' && options !== null) {
    var delay = options.delay;
    if (typeof delay === 'number' && delay > 0) {
       //延遲任務(wù)
      startTime = currentTime + delay;
    } else {
      startTime = currentTime;
    }
  } else {
    startTime = currentTime;
  }
  var timeout;
  //根據(jù)不同優(yōu)先級對應(yīng)時間給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;
  }
  //計算任務(wù)延遲時間(執(zhí)行)
  var expirationTime = startTime + timeout;
  //新任務(wù)初始化
  var newTask = {
    id: taskIdCounter++,
    callback: callback,
    priorityLevel: priorityLevel,
    startTime: startTime,
    expirationTime: expirationTime,
    sortIndex: -1
  };
   //如果startTime大于currentTime則說明優(yōu)先級低,為延遲任務(wù)
  if (startTime > currentTime) {
    // This is a delayed task.
    //將startTime存入新任務(wù),用于任務(wù)排序(執(zhí)行順序)
    newTask.sortIndex = startTime;
    //采用小頂堆,將新任務(wù)插入延遲任務(wù)隊列進行排序
    //當(dāng)前startTime > currentTime所以當(dāng)前任務(wù)為延遲任務(wù)插入延遲任務(wù)隊列
    push(timerQueue, newTask);
    //若可執(zhí)行任務(wù)隊列為空或者新任務(wù)為延遲任務(wù)的第一個
    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.
        //取消延時調(diào)度
        cancelHostTimeout();
      } else {
        isHostTimeoutScheduled = true;
      } // Schedule a timeout.
      requestHostTimeout(handleTimeout, startTime - currentTime);
    }
  } else {
    newTask.sortIndex = expirationTime;
    //推入可執(zhí)行隊列
    push(taskQueue, newTask);
    // wait until the next time we yield.
    //當(dāng)前可調(diào)度無插隊任務(wù)
    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      requestHostCallback(flushWork);//執(zhí)行
    }
  }
  return newTask;
}

從代碼中可以看到Scheduler中的任務(wù)以隊列的形式進行保存分別是 可執(zhí)行隊列taskQueue延遲隊列timerQueue 當(dāng)新任務(wù)進入方法unstable_scheduleCallback會將任放到延遲隊列timerQueue中進行排序(優(yōu)先級依照任務(wù)的sortIndex),如果延遲隊列timerQueue中有任務(wù)變成可執(zhí)行狀態(tài)(currentTmie>startTime)則我們會將任務(wù)放入我們會將任務(wù)取出并放入可執(zhí)行隊列taskQueue并取出最快到期的任務(wù)執(zhí)行

總結(jié)

React是以異步可中斷的更新來替代原有的同步更新,而實現(xiàn)異步可中斷更新的關(guān)鍵是SchedulerScheduler主要的功能是時間切片優(yōu)先級調(diào)度,實現(xiàn)時間切片的關(guān)鍵是requestIdleCallback polyfill,調(diào)度任務(wù)為異步宏任務(wù)。而實現(xiàn)優(yōu)先級調(diào)度的關(guān)鍵是當(dāng)前任務(wù)到期時間,到期時間短的優(yōu)先級更高,根據(jù)任務(wù)的優(yōu)先級分別保存在可執(zhí)行隊列延時隊列;

以上就是React為什么需要Scheduler調(diào)度器原理詳解的詳細(xì)內(nèi)容,更多關(guān)于React Scheduler調(diào)度器原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解在React項目中如何集成和使用web worker

    詳解在React項目中如何集成和使用web worker

    在復(fù)雜的React應(yīng)用中,某些計算密集型或耗時操作可能會阻塞主線程,導(dǎo)致用戶界面出現(xiàn)卡頓或響應(yīng)慢的現(xiàn)象,為了優(yōu)化用戶體驗,可以采用Web Worker來在后臺線程中執(zhí)行這些操作,本文將詳細(xì)介紹在React項目中如何集成和使用Web Worker來改善應(yīng)用性能,需要的朋友可以參考下
    2023-12-12
  • 阿里低代碼框架lowcode-engine自定義設(shè)置器詳解

    阿里低代碼框架lowcode-engine自定義設(shè)置器詳解

    這篇文章主要為大家介紹了阿里低代碼框架lowcode-engine自定義設(shè)置器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • React immer與Redux Toolkit使用教程詳解

    React immer與Redux Toolkit使用教程詳解

    這篇文章主要介紹了React中immer與Redux Toolkit的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-10-10
  • react dva實現(xiàn)的代碼

    react dva實現(xiàn)的代碼

    dva是一個基于redux和redux-saga的數(shù)據(jù)流方案,然后為了簡化開發(fā)體驗,dva額外內(nèi)置了react-router,fetch,可以激烈為一個輕量級的應(yīng)用框架,這篇文章主要介紹了react dva實現(xiàn),需要的朋友可以參考下
    2021-11-11
  • JavaScript React如何修改默認(rèn)端口號方法詳解

    JavaScript React如何修改默認(rèn)端口號方法詳解

    這篇文章主要介紹了JavaScript React如何修改默認(rèn)端口號方法詳解,文中通過步驟圖片解析介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 使用react-native-doc-viewer實現(xiàn)文檔預(yù)覽

    使用react-native-doc-viewer實現(xiàn)文檔預(yù)覽

    這篇文章主要介紹了使用react-native-doc-viewer實現(xiàn)文檔預(yù)覽,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 關(guān)于React狀態(tài)管理的三個規(guī)則總結(jié)

    關(guān)于React狀態(tài)管理的三個規(guī)則總結(jié)

    隨著 JavaScript 單頁應(yīng)用開發(fā)日趨復(fù)雜,JavaScript 需要管理比任何時候都要多的 state (狀態(tài)),這篇文章主要給大家介紹了關(guān)于React狀態(tài)管理的三個規(guī)則,需要的朋友可以參考下
    2021-07-07
  • React類組件和函數(shù)組件對比-Hooks的簡介

    React類組件和函數(shù)組件對比-Hooks的簡介

    Hook?是?React?16.8?的新增特性,它可以讓我們在不編寫class的情況下,?使用state以及其他的React特性(比如生命周期,這篇文章主要介紹了React類組件和函數(shù)組件對比-Hooks的介紹及初體驗,需要的朋友可以參考下
    2022-11-11
  • react+tsx中使用better-scroll詳解

    react+tsx中使用better-scroll詳解

    這篇文章主要介紹了react+tsx中使用better-scroll,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-09-09
  • 基于React實現(xiàn)一個todo打勾效果

    基于React實現(xiàn)一個todo打勾效果

    這篇文章主要為大家詳細(xì)介紹了如何基于React實現(xiàn)一個todo打勾效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03

最新評論