最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React利用scheduler思想實(shí)現(xiàn)任務(wù)的打斷與恢復(fù)

 更新時(shí)間:2024年03月14日 14:21:23   作者:程序員小洛  
這篇文章主要為大家詳細(xì)介紹了React如何利用scheduler思想實(shí)現(xiàn)任務(wù)的打斷與恢復(fù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下

前言

react 16.8版本引入了fiber架構(gòu),在fiber架構(gòu)的支持下,實(shí)現(xiàn)了并發(fā)渲染。

在16.8版本以前,在state改變后,經(jīng)過(guò)一系列的流程,最終會(huì)進(jìn)入diff,在diff的時(shí)候,會(huì)采用DFS進(jìn)行遍歷虛擬dom樹(shù),這個(gè)遍歷過(guò)程是一口氣完成的,當(dāng)我們的應(yīng)用很復(fù)雜,元素比較多的時(shí)候,diff的過(guò)程往往會(huì)花費(fèi)很長(zhǎng)的時(shí)間,主線程此時(shí)正在進(jìn)行diif比較,如果diff事件超過(guò)16.6ms,會(huì)造成瀏覽器掉幀,如果時(shí)間很長(zhǎng),會(huì)造成頁(yè)面卡頓。

在react18版本,默認(rèn)開(kāi)啟了并發(fā)模式,開(kāi)發(fā)者可以使用useTransition hook手動(dòng)開(kāi)并發(fā)模式,在并發(fā)模式下,diff過(guò)程是可中斷的,如果diff時(shí)間超過(guò)5ms,那么React會(huì)中斷此次diff,會(huì)交出主線程的使用權(quán),從而讓瀏覽器去渲染,當(dāng)有空閑的時(shí)間的時(shí)候,會(huì)利用時(shí)間循環(huán)機(jī)制恢復(fù)diif,從而提高大型應(yīng)用的用戶體驗(yàn)。并發(fā)模式下任務(wù)的打斷與恢復(fù)機(jī)制是scheduler提供的,我們今天將探討下,如何利用React scheduler打斷一個(gè)耗時(shí)任務(wù),例如從0累加到2000萬(wàn)。

scheduler

從0累加到2000萬(wàn)

const work = () => {
  for(let currentIndex = 0; currentIndex <  totalCount; currentIndex++) {
    sum += currentIndex
  }
}

火焰圖

我們看上面的火焰圖,這個(gè)任務(wù)執(zhí)行了近100ms,左右,超過(guò)了瀏覽器的一幀16.6ms,當(dāng)一個(gè)任務(wù)執(zhí)行時(shí)間超過(guò)50ms時(shí),瀏覽器會(huì)任務(wù)這個(gè)任務(wù)時(shí)長(zhǎng)任務(wù),超過(guò)50ms的部分,火焰圖會(huì)標(biāo)記為紅色。

既然同步遍歷會(huì)造成瀏覽器掉幀,那么我們有什么辦法做到不讓瀏覽器掉幀呢?答案就是將這2000萬(wàn)個(gè)累加操作分成很小的task,然后讓task作為一個(gè)工作單元去執(zhí)行,當(dāng)執(zhí)行到一定的數(shù)量的時(shí)候,如果運(yùn)行時(shí)間超過(guò)了我們規(guī)定的時(shí)間,那么我們中斷這個(gè)任務(wù),在中斷的時(shí)候判斷下還有沒(méi)有任務(wù)要執(zhí)行,如果有任務(wù)要執(zhí)行,那么我們將這個(gè)任務(wù)執(zhí)行函數(shù)放入事件循環(huán)中,等下一次事件循環(huán)的時(shí)候,取出來(lái)執(zhí)行這個(gè)任務(wù)。

如何讓for循環(huán)中斷呢? 我們只需要在遍歷的時(shí)候判斷當(dāng)前的索引和數(shù)據(jù)總數(shù)以及抽象出一個(gè)方法,判斷應(yīng)不應(yīng)該終止(比如:我規(guī)定讓task累計(jì)運(yùn)行5ms,當(dāng)5ms用完的時(shí)候,就應(yīng)該終止)。

const getCurrentTime = () => Date.now();

// 調(diào)度應(yīng)該被中斷嗎
function shouldYieldToHost() {
  const timeElapsed = getCurrentTime() - startTime;
  // 如果當(dāng)前時(shí)間減去開(kāi)始時(shí)間小于 5ms, 那么繼續(xù)調(diào)度
  if (timeElapsed < 5) {
    return false;
  }
  return true;
}

const work = () => {
  for(let currentIndex = 0; currentIndex <  totalCount && !shouldYieldToHost(); currentIndex++) {
    sum += currentIndex
  }
}

如何判斷有沒(méi)有任務(wù)需要恢復(fù)呢? 我們將work函數(shù)放在workloop函數(shù)中去執(zhí)行,當(dāng)work函數(shù)終止的時(shí)候,調(diào)用?;氐?code>workloop函數(shù)時(shí),判斷currentIndex < totalCount,如果小于,那么我們 reutrn true,告訴調(diào)用workloop的函數(shù)還有任務(wù)需要執(zhí)行,需要將任務(wù)執(zhí)行函數(shù)放到異步任務(wù)中,等下一次事件循環(huán)的時(shí)候取出來(lái)執(zhí)行。

const flushWork = () => {
  return workLoop()
}

const workLoop = () => {
  while(true) {
      try {
        work()
      } catch (error) {
      }finally {
        if(currentIndex < totalCount) {
          return true
        } else {
          return false
        }
      }
  }
}

任務(wù)恢復(fù)機(jī)制,利用事件循環(huán)原理

const performWorkUntilDeadline = () => {
    const currentTime = getCurrentTime();
    startTime = currentTime;
    const hasTimeRemaining = true; // 有剩余時(shí)間

    let hasMoreWork = true;
    try {
      // 這里執(zhí)行的函數(shù)就是 flushWork,flushWork 如果返回一個(gè) true 那么表示還有任務(wù)
      // 這里是 workLoop 循環(huán)里 return 的, 如果 return true, 那么表示還有剩余的任務(wù),只是時(shí)間用完了,被中斷了
       hasMoreWork = flushWork();
    } finally {
      //如果還有剩余任務(wù),調(diào)用schedulePerformWorkUntilDeadline將performWorkUntilDeadline 放入到異步任務(wù)里,等下一次事件循環(huán)被調(diào)用。
      if (hasMoreWork) {
        schedulePerformWorkUntilDeadline();
      } else {
      }
    }
  


let schedulePerformWorkUntilDeadline;
// react 中調(diào)度的優(yōu)先級(jí)  setImmediate > MessageChannel > setTimeout
if (typeof localSetImmediate === 'function') {
  schedulePerformWorkUntilDeadline = () => {
    localSetImmediate(performWorkUntilDeadline);
  };
} else if (typeof MessageChannel !== 'undefined') {
  const channel = new MessageChannel();
  const port = channel.port2;
  channel.port1.onmessage = performWorkUntilDeadline;
  schedulePerformWorkUntilDeadline = () => {
    port.postMessage(null);
  };
} else {
  schedulePerformWorkUntilDeadline = () => {
    localSetTimeout(performWorkUntilDeadline, 0);
  };
}

完整實(shí)現(xiàn)代碼

const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null;
const localClearTimeout =
  typeof clearTimeout === 'function' ? clearTimeout : null;
const localSetImmediate =
  typeof setImmediate !== 'undefined' ? setImmediate : null; 


let startTime; // 記錄開(kāi)始時(shí)間
let sum = 0
const currentIndex = 0;  // 當(dāng)前遍歷的索引
const totalCount = 20000000  

const getCurrentTime = () => Date.now();

// 調(diào)度應(yīng)該被中斷嗎
function shouldYieldToHost() {
  const timeElapsed = getCurrentTime() - startTime;
  // 如果當(dāng)前時(shí)間減去開(kāi)始時(shí)間小于 5ms, 那么繼續(xù)調(diào)度
  if (timeElapsed < 5) {
    return false;
  }
  return true;
}

const performWorkUntilDeadline = () => {
    const currentTime = getCurrentTime();
    startTime = currentTime;
    const hasTimeRemaining = true; // 有剩余時(shí)間

    let hasMoreWork = true;
    try {
      // 這里執(zhí)行的函數(shù)就是 flushWork,flushWork 如果返回一個(gè) true 那么表示還有任務(wù)
      // 這里的 是 workLoop 循環(huán)里 return 的, 如果 return true, 那么表示還有剩余的任務(wù),只是時(shí)間用完了,被中斷了
       hasMoreWork = flushWork();
    } finally {
      if (hasMoreWork) {
        schedulePerformWorkUntilDeadline();
      } else {
      }
    }
  
};

let schedulePerformWorkUntilDeadline;
// react 中調(diào)度的優(yōu)先級(jí)  setImmediate > MessageChannel > setTimeout
if (typeof localSetImmediate === 'function') {
  schedulePerformWorkUntilDeadline = () => {
    localSetImmediate(performWorkUntilDeadline);
  };
} else if (typeof MessageChannel !== 'undefined') {
  const channel = new MessageChannel();
  const port = channel.port2;
  channel.port1.onmessage = performWorkUntilDeadline;
  schedulePerformWorkUntilDeadline = () => {
    port.postMessage(null);
  };
} else {
  schedulePerformWorkUntilDeadline = () => {
    localSetTimeout(performWorkUntilDeadline, 0);
  };
}

const flushWork = () => {
  return workLoop()
}

const workLoop = () => {
  while(true) {
      try {
        work()
      } catch (error) {
      }finally {
        if(currentIndex < totalCount) {
          return true
        } else {
          return false
        }
      }
  }
}

const work = () => {
  for(let currentIndex = 0; currentIndex <  totalCount && !shouldYieldToHost(); currentIndex++) {
    sum += currentIndex
  }
}

performWorkUntilDeadline()

火焰圖

我們可以看到,上面執(zhí)行近100ms的任務(wù)被分成了很多很多小任務(wù),其中每個(gè)小任務(wù)執(zhí)行的時(shí)間是5ms左右。這樣我們就完成了利用react scheduler實(shí)現(xiàn)任務(wù)的打斷與恢復(fù)機(jī)制。

以上就是React利用scheduler思想實(shí)現(xiàn)任務(wù)的打斷與恢復(fù)的詳細(xì)內(nèi)容,更多關(guān)于React scheduler任務(wù)的打斷與恢復(fù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺談從React渲染流程分析Diff算法

    淺談從React渲染流程分析Diff算法

    這篇文章主要介紹了淺談從React渲染流程分析Diff算法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • React高級(jí)指引之Refs and the DOM使用時(shí)機(jī)詳解

    React高級(jí)指引之Refs and the DOM使用時(shí)機(jī)詳解

    在典型的React數(shù)據(jù)流中,props是父組件與子組件交互的唯一方式。要修改一個(gè)子組件,你需要使用新的props來(lái)重新渲染它。但是,在某些情況下,你需要在典型數(shù)據(jù)流之外強(qiáng)制修改子組件
    2023-02-02
  • React實(shí)現(xiàn)單向數(shù)據(jù)流的方法

    React實(shí)現(xiàn)單向數(shù)據(jù)流的方法

    本文主要介紹了React實(shí)現(xiàn)單向數(shù)據(jù)流的方法
    2023-04-04
  • React狀態(tài)管理Redux的使用介紹詳解

    React狀態(tài)管理Redux的使用介紹詳解

    redux是redux官方react綁定庫(kù),能夠使react組件從redux store中讀取數(shù)據(jù),并且向store分發(fā)actions以此來(lái)更新數(shù)據(jù),這篇文章主要介紹了react-redux的設(shè)置,需要的朋友可以參考下
    2022-09-09
  • 深入理解React19中任務(wù)調(diào)度器Scheduler的實(shí)現(xiàn)

    深入理解React19中任務(wù)調(diào)度器Scheduler的實(shí)現(xiàn)

    本文主要介紹了深入理解React任務(wù)調(diào)度器Scheduler的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2026-03-03
  • react項(xiàng)目中使用插件配置路由的方法

    react項(xiàng)目中使用插件配置路由的方法

    這篇文章主要介紹了react項(xiàng)目中使用插件配置路由的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • React路由封裝的實(shí)現(xiàn)淺析

    React路由封裝的實(shí)現(xiàn)淺析

    路由是React項(xiàng)目中相當(dāng)重要的概念,對(duì)于功能較為復(fù)雜的網(wǎng)頁(yè)來(lái)說(shuō),必然會(huì)涉及到不同功能間的頁(yè)面跳轉(zhuǎn),本篇文章將對(duì)React官方維護(hù)的路由庫(kù)React-Router-Dom的使用和常用組件進(jìn)行講解,同時(shí)對(duì)路由組件傳遞param參數(shù)的方式進(jìn)行講解,希望對(duì)各位讀者有所參考
    2022-08-08
  • React+Vite中利用Fetch將CSV數(shù)據(jù)轉(zhuǎn)成JSON字符串

    React+Vite中利用Fetch將CSV數(shù)據(jù)轉(zhuǎn)成JSON字符串

    在一些小型項(xiàng)目中,前端可能需要直接處理 CSV 文件數(shù)據(jù),將其轉(zhuǎn)換為 JSON 字符串后再進(jìn)行邏輯操作和展示,本文將會(huì)介紹兩種方法,需要的朋友可以參考下
    2025-12-12
  • 全棧輕量級(jí)搭配之Remix Prisma Sqlite使用分析

    全棧輕量級(jí)搭配之Remix Prisma Sqlite使用分析

    這篇文章主要為大家介紹了全棧輕量級(jí)搭配之Remix Prisma Sqlite使用示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • 在React中如何優(yōu)雅的處理事件響應(yīng)詳解

    在React中如何優(yōu)雅的處理事件響應(yīng)詳解

    這篇文章主要給大家介紹了關(guān)于在React中如何優(yōu)雅處理事件響應(yīng)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-07-07

最新評(píng)論

凌海市| 南岸区| 三穗县| 乌兰察布市| 桃源县| 绥宁县| 桂阳县| 韶山市| 如皋市| 右玉县| 西安市| 合江县| 秭归县| 石台县| 石狮市| 克拉玛依市| 威海市| 稷山县| 米林县| 资溪县| 敦化市| 乌鲁木齐县| 武平县| 泽库县| 滕州市| 方正县| 平安县| 黑水县| 黄大仙区| 哈密市| 从化市| 湘潭市| 鄂托克前旗| 秦皇岛市| 宜宾县| 北辰区| 霍山县| 自贡市| 舟曲县| 安陆市| 离岛区|