偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

還在用 setTimeout?試試 requestIdleCallback 吧!

系統(tǒng) 瀏覽器
瀏覽器是單線程的,所有任務(wù)都要經(jīng)過事件循環(huán)(Event Loop)來(lái)調(diào)度。當(dāng)你調(diào)用?setTimeout(fn, 0)?時(shí),這個(gè)任務(wù)會(huì)被放進(jìn)?“宏任務(wù)隊(duì)列”?里,只有當(dāng)主線程空出來(lái),才會(huì)去執(zhí)行。

大家好,我是 Sunday。

在開發(fā)中,setTimeout 咱們幾乎天天都在用。

無(wú)論是頁(yè)面初始化后延遲執(zhí)行邏輯、動(dòng)畫間隔,還是接口請(qǐng)求防抖、埋點(diǎn)上報(bào),咱們幾乎都離不開它。

但是 setTimeout 有時(shí)候并不好用,比如說:

  • setTimeout 的執(zhí)行時(shí)間并不準(zhǔn)確,延遲時(shí)間只是任務(wù)準(zhǔn)備已出 EventLoop 的時(shí)間
  • setTimeout 并不會(huì)判斷瀏覽器任務(wù)是否空閑,從而當(dāng)任務(wù)執(zhí)行時(shí)可能會(huì)出現(xiàn)卡頓的情況

瀏覽器是單線程的,所有任務(wù)都要經(jīng)過事件循環(huán)(Event Loop)來(lái)調(diào)度。當(dāng)你調(diào)用 setTimeout(fn, 0) 時(shí),這個(gè)任務(wù)會(huì)被放進(jìn) “宏任務(wù)隊(duì)列” 里,只有當(dāng)主線程空出來(lái),才會(huì)去執(zhí)行。

因此,如果我們想要在 瀏覽器空閑時(shí)間 去執(zhí)行一些大任務(wù)操作(比如:埋點(diǎn)上報(bào)),那么 setTimeout 并不方便。

那么,有沒有一個(gè)更加聰明的 API,可以知道瀏覽器什么時(shí)候會(huì)空閑,從而可以 自動(dòng)調(diào)用 任務(wù)呢?

它就是 requestIdleCallback。

requestIdleCallback

圖片圖片

requestIdleCallback 的核心是  瀏覽器級(jí)空閑調(diào)度 API,它能讓你把一些非關(guān)鍵任務(wù)放到瀏覽器“閑”的時(shí)候去執(zhí)行,從而讓關(guān)鍵任務(wù)(如渲染、動(dòng)畫、交互)始終保持流暢。PS: 也就是說,瀏覽器在處理完一幀的渲染、動(dòng)畫、事件之后,如果還有空余時(shí)間,就會(huì)來(lái)執(zhí)行你的任務(wù)。

這個(gè)函數(shù)接收兩個(gè)參數(shù) callback, options,并且會(huì)返回一個(gè) ID 作為結(jié)束回調(diào)參數(shù)(通過 Window.cancelIdleCallback() 結(jié)束回調(diào))。

圖片圖片

基礎(chǔ)應(yīng)用

比如說:咱們要做一個(gè)埋點(diǎn)上報(bào)的系統(tǒng),希望在用戶瀏覽頁(yè)面后,上報(bào)一些埋點(diǎn)日志。

sendAnalyticsData() // 立即上報(bào)埋點(diǎn)

如果代碼這么寫,則當(dāng)前代碼會(huì)在頁(yè)面加載階段就發(fā)請(qǐng)求,不僅占用主線程,還可能影響首屏性能。

那么如果使用 requestIdleCallback ,則可以等到瀏覽器“閑”下來(lái)再去上報(bào)。

requestIdleCallback(() => {
  sendAnalyticsData()
})

這就是一個(gè)典型的“低優(yōu)先級(jí)任務(wù)”場(chǎng)景。用 requestIdleCallback,讓瀏覽器自動(dòng)幫咱們排好優(yōu)先順序。

利用 deadline 拆解任務(wù)

此時(shí),假設(shè)我們有一個(gè)很大的任務(wù),比如:需要遍歷十萬(wàn)條數(shù)據(jù)進(jìn)行處理。

const arr = Array.from({ length: 100000 }, (_, i) => i)

function task() {
  while (arr.length > 0) {
    helloSunday(arr.shift())
  }
}

function helloSunday(i) {
  console.log('hello', i)
}

task()

如果咱們直接這樣寫代碼,那么在 企業(yè)項(xiàng)目 中,因?yàn)檫€需要處理更多的額外任務(wù),那么就一定會(huì)導(dǎo)致頁(yè)面嚴(yán)重卡頓,因?yàn)?JavaScript 是單線程的,這段任務(wù)會(huì)一直占著主線程不放。

而換成 requestIdleCallback,我們可以利用 deadline.timeRemaining() 檢查當(dāng)前幀的“空閑時(shí)間”,把任務(wù)拆成多次執(zhí)行。

  • deadline:瀏覽器傳入的對(duì)象,包含當(dāng)前幀的剩余空閑時(shí)間
  • deadline.timeRemaining():表示當(dāng)前幀還剩多少毫秒可以安全執(zhí)行任務(wù)
  • deadline.didTimeout:表示任務(wù)是否超時(shí)(當(dāng)設(shè)置了 timeout 時(shí),才會(huì)有用)
<body>
  <div>測(cè)試</div>
  <button onclick="renderClick()">點(diǎn)擊,進(jìn)行大量渲染</button>

  <script>
    const arr = Array.from({ length: 100000 }, (_, i) => i)

    function workLoop(deadline) {
      // 有安全執(zhí)行時(shí)間時(shí),才會(huì)執(zhí)行
      while (deadline.timeRemaining() > 0 && arr.length > 0) {
        helloSunday(arr.shift())
      }

      if (arr.length > 0) {
        // 再次觸發(fā)空閑回調(diào)
        requestIdleCallback(workLoop)
      }
    }

    function helloSunday(i) {
      console.log('hello', i)
    }

    requestIdleCallback(workLoop)

    // 渲染大量的 div
    function renderClick() {
      for (let i = 0; i < 50000; i++) {
        const div = document.createElement('div')
        div.textContent = `點(diǎn)擊渲染的元素 ${i}`
        document.body.appendChild(div)
      }
    }
    //  直接渲染
    renderClick()
  </script>
</body>

通過以上代碼,咱們就可以測(cè)試出,在一開始瀏覽器忙的時(shí)候,requestIdleCallback 不會(huì)執(zhí)行。當(dāng)瀏覽器空閑下來(lái)之后,才會(huì)進(jìn)行處理。

咱們可以通過以下的表格,來(lái)對(duì)比下兩個(gè)函數(shù)的區(qū)別:

對(duì)比項(xiàng)

setTimeout

requestIdleCallback

調(diào)度方式

固定時(shí)間

主線程空閑時(shí)

精準(zhǔn)度

不穩(wěn)定,受任務(wù)隊(duì)列影響

智能調(diào)度,由瀏覽器控制(除非設(shè)置了 timeout)

性能表現(xiàn)

容易卡頓

平滑、不打斷渲染

適合場(chǎng)景

動(dòng)畫延遲、節(jié)流防抖

預(yù)加載、日志、數(shù)據(jù)緩存、計(jì)算任務(wù)

requestIdleCallback vs requestAnimationFrame

說完 requestIdleCallback,很多同學(xué)可能會(huì)想:它和 requestAnimationFrame(簡(jiǎn)稱 rAF)是不是差不多????jī)蓚€(gè)名字都帶 request,還都和瀏覽器時(shí)機(jī)有關(guān)。

其實(shí),它們的目標(biāo)是完全不同的。

  • requestAnimationFrame:關(guān)注 渲染幀,保證動(dòng)畫和刷新同步。他會(huì)在 下一幀繪制前 調(diào)用,用來(lái)驅(qū)動(dòng)動(dòng)畫。
  • requestIdleCallback:關(guān)注 空閑幀,在主線程空閑時(shí)執(zhí)行任務(wù)。他會(huì)在 瀏覽器空閑時(shí) 調(diào)用,用來(lái)執(zhí)行非關(guān)鍵任務(wù)。

兩者的典型場(chǎng)景

requestAnimationFrame:動(dòng)畫、位移動(dòng)效。

function moveBox() {
  box.style.left = box.offsetLeft + 2 + 'px'
  requestAnimationFrame(moveBox)
}
requestAnimationFrame(moveBox)

這類任務(wù)要求和屏幕刷新頻率保持一致(比如:60fps),否則就會(huì)掉幀或卡頓,所以必須放在 rAF 中執(zhí)行。


例如:滾動(dòng)聯(lián)動(dòng)、進(jìn)度條、骨架屏、loading 動(dòng)畫等。

requestIdleCallback:后臺(tái)任務(wù)、預(yù)加載。

requestIdleCallback((deadline) => {
  while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    helloSunday(tasks.shift())
  }
})

這類任務(wù)對(duì)時(shí)機(jī)要求不高,重點(diǎn)是不影響渲染。當(dāng)瀏覽器一幀執(zhí)行完、空出一點(diǎn)時(shí)間,它就會(huì)去做這些工作。


例如:日志上報(bào)、預(yù)取緩存、離線計(jì)算、大數(shù)據(jù)分片等。

Polyfill 與兼容性方案

目前,requestIdleCallback 并不是所有瀏覽器都支持,尤其是 Safari 和部分移動(dòng)端 WebView。

但沒關(guān)系,我們可以自己實(shí)現(xiàn)一個(gè)簡(jiǎn)易版本(Polyfill),通過 setTimeout 來(lái)模擬「空閑回調(diào)」的效果。

// 如果瀏覽器原生不支持 requestIdleCallback,則定義一個(gè)兼容版本
if (!window.requestIdleCallback) {
  window.requestIdleCallback = function (cb) {
    // 記錄當(dāng)前時(shí)間,用于計(jì)算剩余空閑時(shí)間
    const start = Date.now()

    // 使用 setTimeout 模擬空閑調(diào)度
    // 在 1 毫秒后異步執(zhí)行回調(diào)函數(shù) cb
    return setTimeout(() => {

      // 手動(dòng)構(gòu)造一個(gè) deadline 對(duì)象,模擬瀏覽器傳入的參數(shù)
      cb({
        // 表示任務(wù)是否超時(shí)(這里固定為 false,因?yàn)闆]有 timeout 機(jī)制)
        didTimeout: false,

        // timeRemaining 用于返回當(dāng)前幀還剩下多少“空閑時(shí)間”(毫秒)
        // 假設(shè)一幀 50ms(對(duì)應(yīng) 20fps),
        // 當(dāng)前時(shí)間 - start 表示已經(jīng)消耗的時(shí)間,
        // 50 - 已消耗時(shí)間 = 剩余可用時(shí)間
        // 若結(jié)果為負(fù),則取 0,避免返回負(fù)值
        timeRemaining: function () {
          return Math.max(0, 50 - (Date.now() - start))
        }
      })
    }, 1) // 延遲 1ms 調(diào)用,避免阻塞主線程
  }
}

// 如果瀏覽器不支持 cancelIdleCallback,則提供對(duì)應(yīng)的取消方法
if (!window.cancelIdleCallback) {
  window.cancelIdleCallback = function (id) {
    // 直接調(diào)用 clearTimeout 取消 setTimeout 模擬的任務(wù)
    clearTimeout(id)
  }
}

雖然這種方式無(wú)法真正識(shí)別主線程空閑時(shí)間,但在不支持 requestIdleCallback 的瀏覽器中,可以保證代碼結(jié)構(gòu)一致、功能不報(bào)錯(cuò)。

責(zé)任編輯:武曉燕 來(lái)源: 程序員Sunday
相關(guān)推薦

2021-12-06 17:44:56

MHAMySQL高可用

2024-10-28 10:55:50

Jedis組件客戶端

2021-04-21 10:36:47

StringBuildJava8StringJoine

2023-02-27 08:53:54

JedislettuceRedis

2020-12-07 05:50:54

print()Python代碼

2024-03-26 10:30:37

Mybatis擴(kuò)展庫(kù)API

2019-09-21 21:32:34

數(shù)據(jù)庫(kù)SQL分布式

2023-02-01 10:40:01

2010-06-24 09:38:42

Windows備份云平臺(tái)

2012-07-19 10:03:32

2013-04-18 09:43:34

碼農(nóng)網(wǎng)站網(wǎng)站設(shè)計(jì)

2021-06-10 10:33:22

Jenkins持續(xù)集成工具自動(dòng)化

2024-11-12 16:28:34

2024-01-23 13:20:00

分庫(kù)分表分布式

2024-04-11 09:17:51

ArraysJava安全

2021-10-14 18:15:38

BeanUtils對(duì)象生成器

2025-09-08 04:00:00

2024-06-03 00:00:06

高性能數(shù)據(jù)傳輸應(yīng)用程序

2020-08-03 09:40:39

Python編程語(yǔ)言Instagram

2021-01-03 17:14:16

ORMObjective S運(yùn)行
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)