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

react時間分片實現流程詳解

 更新時間:2022年11月25日 14:34:29   作者:flyzz177  
實現react時間分片,主要內容包括什么是時間分片、為什么需要時間分片、實現分片開啟 - 固定、實現分片中斷、重啟 - 連續(xù)、分片重啟、實現延遲執(zhí)行 - 有間隔、時間分片異步執(zhí)行方案的演進、時間分片簡單實現、總結、基本概念、基礎應用、原理機制和需要注意的事項等

我們常說的調度,可以分為兩大模塊,時間分片和優(yōu)先級調度

  • 時間分片的異步渲染是優(yōu)先級調度實現的前提
  • 優(yōu)先級調度在異步渲染的基礎上引入優(yōu)先級機制控制任務的打斷、替換。

本節(jié)將從時間分片的實現剖析react的異步渲染原理,閱讀本文你講可以了解

  • 時間分片是什么
  • 為什么需要時間分片
  • 時間分片在react中是如何運行的
  • 時間分片的極簡實現

什么是時間分片

上文提到過,時間分片其實就是一個固定而連續(xù)且有間隔的時間區(qū)間

固定:時間分片是工作時長是固定的
連續(xù):分片之間是連續(xù)的,當前分片內有工作沒做完,會留到下個分片繼續(xù)
有間隔:在進入下一個分片前,會有一定時間的間隔

這些解釋比較抽象,可以更加通俗去理解

固定:每天固定工作8小時
連續(xù):每天都要上班
有間隔:明天上班前會休息一段時間

為什么需要時間分片

我們知道,react最重要,也是最耗時的任務是節(jié)點遍歷。

設想一個頁面上有一萬個DOM節(jié)點,如果我們用同步的方式一個個遍歷完需要花費多少時間。而且如果是同步遍歷的話,遍歷的過程中,JS線程一直會霸占主線程,導致阻塞了瀏覽器的其他線程,導致卡頓的情況出現。

換個思路解決這個遍歷問題,能不能遍歷一會,休息一會,休息的過程中就可以把主線程交還給渲染線程和事件線程,這樣就能及時渲染節(jié)點和響應用戶事件,避免造成卡頓。

為了實現遍歷一會,休息一會,我們可以將整個過程分解為以下三個步驟

  • 分片開啟
  • 分片中斷、分片重啟
  • 延遲執(zhí)行

這三個步驟與時間分片的三個特性一一對應

實現分片開啟 - 固定

時間分片是獨立于React的節(jié)點遍歷流程的,所以只需要把節(jié)點遍歷的入口函數以回調函數的形式傳入即可,這樣就可以讓時間分片來決定節(jié)點遍歷執(zhí)行時機。

// 節(jié)點遍歷的入口函數
function Reconcile協調() {
    節(jié)點遍歷()
}
function Schedule調度() {
    創(chuàng)建分片(Reconcile協調)
}

第一步,需要將時間分片要調度的函數抽象為一個任務對象

function 創(chuàng)建分片(需要被調度的函數) {
    const 新的任務 = {
        callback: 需要被調度的函數
    }
}

第二步,設定分片工作時長,為了方便后續(xù),可以直接計算過期時間。分片工作時長一般為5ms,但Scheduler會根據任務優(yōu)先級有所調整,這里為了更好理解,先默認5ms。

const taskQueue = []
function 創(chuàng)建分片(需要被調度的函數) {
    const 新的任務 = {
        callback: 需要被調度的函數,
        expirationTime: performance.now() + 5000
    }
    taskQueue.push(新的任務)
    發(fā)起異步調度()
}

每次分片的創(chuàng)建其實都是新一輪調度的開始,所以在末尾會發(fā)起異步調度

為什么用performance.now()而不用Date.now()

performance.now()返回當前頁面的停留時間,Date.now()返回當前系統(tǒng)時間。但不同的是performance.now()精度更高,且比Date.now()更可靠

  • performance.now()返回的是微秒級的,Date.now()只是毫秒級
  • performance.now()一個恒定的速率慢慢增加的,它不會受到系統(tǒng)時間的影響。Date.now()受到系統(tǒng)時間影響,系統(tǒng)時間修改Date.now()也會改變

實現分片中斷、重啟 - 連續(xù)

分片中斷

我們在第一章已經將React的虛擬DOM結構從樹形結構優(yōu)化成鏈表結構,所以能輕松使用while循環(huán)實現可中斷的遍歷

那么如果要將遍歷任務時間分片相結合,且實現分片中斷功能的話,只需要在while循環(huán)出加入分片時間過期的校驗即可

function 分片過期校驗() {
    return (perfromance.now() - 分片開啟時間) >= 5000
}
let 需要被遍歷的幸運兒節(jié)點 = null
function 構建節(jié)點() {
    /** * ...在這里進行節(jié)點構建工作 */
    需要被遍歷的幸運兒節(jié)點 = 需要被遍歷的幸運兒節(jié)點.next
}
function 節(jié)點遍歷() {
    while (需要被遍歷的幸運兒節(jié)點 != null && !分片過期校驗()) {
        構建節(jié)點()
    }
}
function Schedule調度() {
    創(chuàng)建分片(Reconcile協調)
}

分片重啟

分片重啟意思就是上一輪時間分片因為過期中斷了,需要重新發(fā)起一輪時間分片。

實現的思路是,在上一輪分片結束之后判斷是否還需要開啟下一輪分片,需要的話則重新發(fā)起一輪異步調度即可,相關參考視頻講解:進入學習

function 分片過期校驗() {
    return (perfromance.now() - 分片開啟時間) >= 5000
}
function 分片事件循環(huán)() {
    let 棧頂任務 = taskQueue.peek()

    while (棧頂任務) {
        if (分片過期校驗()) break
        const 棧頂任務回調 = 棧頂任務.callback()
        if (typeof 棧頂任務回調 == 'function') {
            // 當前任務還沒有執(zhí)行完,繼續(xù)搞
            棧頂任務.callback = 棧頂任務回調
        } else {
            // 當前任務已執(zhí)行完,彈出隊列
            taskQueue.pop()
        }
        棧頂任務 = taskQueue.peek()
    }
    // 還有任務哦
    if (棧頂任務) return true
    return false
}
function 分片執(zhí)行() {
    分片開啟時間 = performance.now()
    var 是否還有任務未執(zhí)行完畢
    try {
        是否還有任務未執(zhí)行完畢 = 分片事件循環(huán)()
    } finally {
        // 分片重啟
        if (是否還有任務未執(zhí)行) 發(fā)起異步調度()
    }
}
function 發(fā)起異步調度() {
    // 這里實際上是異步執(zhí)行,看下面有間隔
    分片執(zhí)行()
}

重啟的條件就是判斷分片任務隊列中是否還有任務,有的話就發(fā)起下一輪的時間分片

實現延遲執(zhí)行 - 有間隔

有間隔的本質是延遲JS的執(zhí)行,讓瀏覽器有喘息的時間,去處理其他線程的任務,哪如何把主線程控制權交還給瀏覽器呢??

可以使用異步特性發(fā)起下一輪時間分片,實現延遲執(zhí)行

function 發(fā)起異步調度() {
    // 將主線程短暫的交還給瀏覽器
    setTimeout(() => {
        分片執(zhí)行()
    }, 0)
}

為什么選擇宏任務實現異步執(zhí)行

微任務無法真正達到交還主線程控制權的要求。

因為一輪事件循環(huán),是先執(zhí)行一個宏任務,然后再清空微任務隊列里面的任務,如果在清空微任務隊列的過程中,依然有新任務插入到微任務隊列中的話,還是把這些任務執(zhí)行完畢才會釋放主線程。所以微任務不合適。

時間分片異步執(zhí)行方案的演進

為什么不是setTimeout

因為setTimeout的遞歸層級過深的話,延遲就不是1ms,而是4ms,這樣會造成延遲時間過長

為什么不是requestAnimationFrame?

requestAnimationFramed是在微任務執(zhí)行完之后,瀏覽器重排重繪之前執(zhí)行,執(zhí)行的時機是不準確的。如果raf之前JS的執(zhí)行時間過長,依然會造成延遲

為什么不是requestIdleCallback

requestIdleCallback的執(zhí)行時機是在瀏覽器重排重繪之后,也就是瀏覽器的空閑時間執(zhí)行。其實執(zhí)行的時機依然是不準確的,raf執(zhí)行的JS代碼耗時可能會過長

為什么是 MessageChannel?

MessageChannel的執(zhí)行時機比setTimeout靠前

在React中,異步執(zhí)行優(yōu)先使用setImmediate,其次是MessageChannel,最后是setTimeout,都是根據瀏覽器對這些的特性支持程度決定的。

時間分片簡單實現

下面會整合上面的所有代碼,模擬出最簡單的時間分片實現(不包含優(yōu)先級機制)

Scheduler.js

const taskQueue = []
let 分片開啟時間 = -1
// **時間分片核心**
const 分片過期校驗 = () => {
    return (perfromance.now() - 分片開啟時間) >= 5000
}
function 分片事件循環(huán)() {
    let 棧頂任務 = taskQueue.peek()
    while (棧頂任務) {
        // 每執(zhí)行完一個任務,都要校驗一下分片是否過期
        if (分片過期校驗()) break
        const 棧頂任務回調 = 棧頂任務.callback()
        if (typeof 棧頂任務回調 == 'function') {
            // 當前任務還沒有執(zhí)行完,繼續(xù)搞
            棧頂任務.callback = 棧頂任務回調
        } else {
            // 當前任務已執(zhí)行完,彈出隊列
            taskQueue.pop()
        }
        棧頂任務 = taskQueue.peek()
    }
    // 還有任務哦
    if (棧頂任務) return true
    return false
}
function 分片執(zhí)行() {
    分片開啟時間 = performance.now()
    var 是否還有任務未執(zhí)行完畢
    try {
        是否還有任務未執(zhí)行完畢 = 分片事件循環(huán)()
    } finally {
        // **時間分片核心:分片重啟**
        if (是否還有任務未執(zhí)行) 發(fā)起異步調度()
    }
}
// 實例化 MessageChannel
const channel = new MessageChannel()
const port2 = channel.port2
channel.port1.onmessage = 分片執(zhí)行
function 發(fā)起異步調度() {
    // 向通道1發(fā)消息,通道1收到消息就會執(zhí)行分片任務
    // **時間分片核心:延遲執(zhí)行**
    port2.postMessage(null)
}
function 創(chuàng)建分片(需要被調度的函數) {
    // **時間分片核心:分片開啟**
    const 新的任務 = {
        callback: 需要被調度的函數,
        expirationTime: performance.now() + 5000
    }
    taskQueue.push(新的任務)
    發(fā)起異步調度()
}
export default {
    創(chuàng)建分片,
    分片過期校驗
}

ReactDOM.js

import * as Scheduler from './Scheduler'
const {
    創(chuàng)建分片,
    分片過期校驗
} = Scheduler
let 需要被遍歷的幸運兒節(jié)點 = null
function 構建節(jié)點() {
    /** * ...在這里進行節(jié)點構建工作 */
    需要被遍歷的幸運兒節(jié)點 = 需要被遍歷的幸運兒節(jié)點.next
}
function 節(jié)點遍歷() {
    // **時間分片核心:分片中斷**
    while (需要被遍歷的幸運兒節(jié)點 != null && !分片過期校驗()) {
        構建節(jié)點()
    }
}
function Schedule調度() {
    創(chuàng)建分片(Reconcile協調)
}
function 調度入口() {
    需要被遍歷的幸運兒節(jié)點 = react應用根節(jié)點
    Schedule調度()
}
調度入口()

這段時間分片的偽代碼相對于react中源碼的實現,少了很多邏輯判斷,并且集中了起來,應該會相對好理解很多。

如果還是覺得有點晦澀,可以重點關注偽代碼中標有時間分片核心注釋的代碼,結合上文提到的概念理解

總結

讀完這篇文章估計你可能對時間分片的概念已經有所有了解了,是不是覺得react16的新特性之一時間分片,也并沒有想象中的神秘。

總的下來,時間分片就是由簡單的三個模塊組成:

  • 分片開啟
  • 分片中斷、重啟
  • 延遲執(zhí)行

時間分片是Scheduler調度器兩大特性中的一個,另一個是任務的優(yōu)先級調度,接下來可能會花兩到三篇的篇幅去講解。在源碼閱讀的過程中,我覺得時間分片的實現已經非常驚艷了,沒想到后面優(yōu)先級調度的設計對我更是無可匹敵的沖擊。

到此這篇關于react時間分片實現流程詳解的文章就介紹到這了,更多相關react時間分片內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • React中使用Echarts無法顯示title、tooltip等組件的解決方案

    React中使用Echarts無法顯示title、tooltip等組件的解決方案

    這篇文章主要介紹了React中使用Echarts無法顯示title、tooltip等組件的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • react中實現修改input的defaultValue

    react中實現修改input的defaultValue

    這篇文章主要介紹了react中實現修改input的defaultValue方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • React-intl 實現多語言的示例代碼

    React-intl 實現多語言的示例代碼

    本篇文章主要介紹了React-intl 實現多語言的示例代碼,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • create-react-app常用自定義配置教程示例

    create-react-app常用自定義配置教程示例

    這篇文章主要為大家介紹了create-react-app常用自定義配置教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • React如何配置src根目錄@

    React如何配置src根目錄@

    這篇文章主要介紹了React如何配置src根目錄@,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2024-01-01
  • 使用React實現內容滑動組件效果

    使用React實現內容滑動組件效果

    這篇文章主要介紹了使用React實現一個內容滑動組件效果,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • 基于React實現下拉刷新效果

    基于React實現下拉刷新效果

    這篇文章主要介紹了如何基于react實現下拉刷新效果,在下拉的時候會進入loading狀態(tài),文中有詳細的代碼示例供大家參考,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-03-03
  • 作為老司機使用 React 總結的 11 個經驗教訓

    作為老司機使用 React 總結的 11 個經驗教訓

    這篇文章主要介紹了作為老司機使用 React 總結的 11 個經驗教訓,需要的朋友可以參考下
    2017-04-04
  • React Hook用法示例詳解(6個常見hook)

    React Hook用法示例詳解(6個常見hook)

    這篇文章主要介紹了React Hook用法詳解(6個常見hook),本文通過實例代碼給大家介紹了6個常見hook,需要的朋友可以參考下
    2021-04-04
  • react的context和props詳解

    react的context和props詳解

    這篇文章主要介紹了react的context和props的相關資料,幫助大家更好的理解和學習使用React,感興趣的朋友可以了解下
    2021-11-11

最新評論