日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

瀏覽器也擁有了原生的 “時間切片” 能力!

來源: 責編: 時間:2024-02-02 16:55:49 228觀看
導讀大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心~就在 Chrome 115 版本,瀏覽器開始了對 scheduler.yield 的灰度測試。scheduler.yield 是 scheduler API 中新增的一個功能,它

大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心~Ljl28資訊網——每日最新資訊28at.com

就在 Chrome 115 版本,瀏覽器開始了對 scheduler.yield 的灰度測試。scheduler.yield 是 scheduler API 中新增的一個功能,它能以更簡單、更好的方式將控制權交還給主線程。在開始講解這個 API 之前,我們先來看一個新的性能指標。Ljl28資訊網——每日最新資訊28at.com

Ljl28資訊網——每日最新資訊28at.com

下次繪制交互 (INP)

下次繪制交互 (INP) 是一項新的指標,瀏覽器計劃于 2024 年 3 月將其取代取代首次輸入延遲 (FID) ,成為最新的 Web Core Vitals(Web 核心性能指標,可以看我這篇文章:解讀新一代 Web 性能體驗和質量指標)。Ljl28資訊網——每日最新資訊28at.com

Chrome 使用數據顯示,用戶在頁面上花費的時間有 90% 是在網頁加載完成后花費的,因此,仔細測量整個頁面生命周期的響應能力是非常重要的,這就是 INP 指標評估的內容。Ljl28資訊網——每日最新資訊28at.com

良好的響應能力意味著頁面可以快速響應并且與用戶進行的交互。當頁面響應交互時,最直接的結果就是視覺反饋,由瀏覽器在瀏覽器渲染的下一幀中體現。例如,視覺反饋會告訴我們是否確實添加了購物車的商品、是否快讀打開了導航菜單、服務器是否正在對登錄表單的內容進行身份驗證等等。INP 的目標就是確保對于用戶進行的所有或大多數交互,從用戶發起交互到繪制下一幀的時間盡可能短。Ljl28資訊網——每日最新資訊28at.com

INP 是一種指標,通過觀察用戶訪問頁面的整個生命周期中發生的所有單擊、敲擊和鍵盤交互的延遲來評估頁面對用戶交互的整體響應能力。Ljl28資訊網——每日最新資訊28at.com

Ljl28資訊網——每日最新資訊28at.com

交互是在同一邏輯用戶手勢期間觸發的一組事件處理程序。例如,觸摸屏設備上的 “點擊” 交互包括多個事件,例如 pointerup、pointerdown 和 click。交互可以由 JavaScript、CSS、內置瀏覽器控件或其組合驅動。Ljl28資訊網——每日最新資訊28at.com

Ljl28資訊網——每日最新資訊28at.com

交互的延遲就是由驅動交互的這一組事件處理程序的單個最長持續時間組成的,從用戶開始交互到渲染下一幀視覺反饋的時間。Ljl28資訊網——每日最新資訊28at.com

INP 考慮的是所有頁面的交互,而首次輸入延遲 (FID) 只會考慮第一次交互。而且它只測量了第一次交互的輸入延遲,而不是運行事件處理程序所需的時間或下一幀渲染的延遲。Ljl28資訊網——每日最新資訊28at.com

瀏覽器希望使用 INP 替代 FID 就意味著用戶的交互體驗越來越重要了,我們常常聽到的時間切片的概念,實際上就是為了提升網頁的交互響應能力。Ljl28資訊網——每日最新資訊28at.com

時間切片

JavaScript 使用 run-to-completion 模型來處理任務。這意味著,當任務在主線程上運行時,該任務將運行必要的時間才能完成。任務完成后,控制權交會還給主線程,這樣主線程就可以處理隊列中的下一個任務。Ljl28資訊網——每日最新資訊28at.com

除了任務永遠不會完成的極端情況(例如無限循環)之外,屈服是 JavaScript 任務調度邏輯不可避免的一個方面。屈服遲早會發生,只是時間問題,而且越早越好。當任務運行時間過長(準確地說超過 50 毫秒)時,它們會被視為長任務。Ljl28資訊網——每日最新資訊28at.com

長任務是頁面響應能力差的一個根源,因為它們延遲了瀏覽器響應用戶輸入的能力。長任務發生的次數越多,而且運行的時間越長,用戶就越有可能感覺到頁面運行緩慢,甚至感覺頁面完全掛掉了。Ljl28資訊網——每日最新資訊28at.com

不過,代碼在瀏覽器中啟動任務并不意味著必須等到任務完成后才能將控制權交還給主線程。你可以通過在任務中明確交出控制權來提高對頁面上用戶輸入的響應速度,這樣就能在下一個合適的時間來完成任務。這樣,其他任務就能更快地在主線程上獲得時間,而不必等待長任務的完成。Ljl28資訊網——每日最新資訊28at.com

Ljl28資訊網——每日最新資訊28at.com

這張圖可以很直觀的顯示:在上面的執行中,只有在任務運行完成后才會交還控制權,這意味著任務可能需要更長時間才能完成,然后才會將控制權交還給主線程。在下面,控制權交還是主動進行的,將一個較長的任務分解成多個較小的任務。這樣,用戶交互可以更快地運行,從而提高輸入響應速度和 INP。Ljl28資訊網——每日最新資訊28at.com

當我們想要明確屈服時,就是在告訴瀏覽器 “嘿,我知道我要做的工作可能需要一段時間,并且我不希望你在響應用戶輸入之前必須完成所有這些工作或其他可能也很重要的任務”。Ljl28資訊網——每日最新資訊28at.com

聽起來這個是不是很熟悉?這其實就是我們常說的 “時間切片” 的概念,之前你聽到可能還是在 React 的理念里,因為它是最早提出這個能力的前端框架。我們再來回顧下面這個典型的例子:Ljl28資訊網——每日最新資訊28at.com

舊版 React 架構是遞歸同步更新的,如果節點非常多,即使只有一次 state 變更,React 也需要進行復雜的遞歸更新,更新一旦開始,中途就無法中斷,直到遍歷完整顆樹,才能釋放主線程。Ljl28資訊網——每日最新資訊28at.com

Ljl28資訊網——每日最新資訊28at.com

當渲染的層級很深時,遞歸更新時間超過了16ms,如果這時有用戶操作或動畫渲染等,就會表現為卡頓:Ljl28資訊網——每日最新資訊28at.com

Ljl28資訊網——每日最新資訊28at.com

后來,React 實現了自己的 Scheduler ,它可以將一次耗時很長的更新任務被拆分成一小段一小段的。這樣瀏覽器就有剩余時間執行樣式布局和樣式繪制,減少掉幀的可能性。Ljl28資訊網——每日最新資訊28at.com

Ljl28資訊網——每日最新資訊28at.com

每個小的任務完成后,控制權就會交還給主線程,瀏覽器就有了時間去及時的完成用戶的交互或頁面的繪制,所以頁面會很絲滑:Ljl28資訊網——每日最新資訊28at.com

Ljl28資訊網——每日最新資訊28at.com

這個思路太棒了,在原生的 JavaScript 代碼,或者其他框架中我們也想要這樣的能力怎么辦?Ljl28資訊網——每日最新資訊28at.com

使用 setTimeout

一種常見的過渡方法是使用時間為 0 的 setTimeout。這種方法之所以有效,是因為傳遞給 setTimeout 的回調會將剩余工作轉移到一個單獨的任務中,這個任務將排隊等待后續執行,這樣也可以實現把一大塊工作分成更小的部分。Ljl28資訊網——每日最新資訊28at.com

但是,使用 setTimeout 進行屈服可能會帶來不良的副作用:屈服之后的工作將進入任務隊列的最尾部。通過用戶交互安排的任務仍會排在任務隊列的前面,但你想做的剩余工作可能會被排在它前面的其他任務進一步延遲。Ljl28資訊網——每日最新資訊28at.com

我們可以看一個下面的示例:Ljl28資訊網——每日最新資訊28at.com

function blockingTask (ms = 200) {  let arr = [];  const blockingStart = performance.now();  console.log(`Synthetic task running for ${ms} ms`);  while (performance.now() < (blockingStart + ms)) {    arr.push(Math.random() * performance.now / blockingStart / ms);  }}function yieldToMain () {  return new Promise(resolve => {    setTimeout(resolve, 0);  });}async function runTaskQueueSetTimeout () {  if (typeof intervalId === "undefined") {    alert("Click the button to run blocking tasks periodically first.");        return;  }    clearTaskLog();  for (const item of [1, 2, 3, 4, 5]) {    blockingTask();    logTask(`Processing loop item ${item}`);        await yieldToMain();  }}document.getElementById("setinterval").addEventListener("click", ({ target }) => {  clearTaskLog();  intervalId = setInterval(() => {    if (taskOutputLines < MAX_TASK_OUTPUT_LINES) {      blockingTask();          logTask("Ran blocking task via setInterval");    }  });    target.setAttribute("disabled", true);}, {  once: true});document.getElementById("settimeout").addEventListener("click", () => {  runTaskQueueSetTimeout();});

我們先通過 setinterval 來定期執行一些任務,下面我們來使用 setTimeout 來模擬時間切片,將長任務進行拆解,我們會得到下面這樣的打印結果:Ljl28資訊網——每日最新資訊28at.com

Processing loop item 1Processing loop item 2Ran blocking task via setIntervalProcessing loop item 3Ran blocking task via setIntervalProcessing loop item 4Ran blocking task via setIntervalProcessing loop item 5Ran blocking task via setIntervalRan blocking task via setInterval

很多腳本(尤其是第三方腳本)經常會注冊一個定時器函數,在某個時間間隔內運行工作。使用 setTimeout 來拆解長任務意味著,來自其他任務源的工作可能會排在退出事件循環后必須完成的剩余工作之前。Ljl28資訊網——每日最新資訊28at.com

這也許能夠起到一定的作用,但在許多情況下,這種行為是開發者不愿輕易放棄主線程控制權的原因。能主動交出控制權是好事,因為用戶交互有機會更快地運行,但它也會讓其他非用戶交互的工作在主線程上獲得時間。這確實是個問題,scheduler.yield 可以幫助解決這個問題!Ljl28資訊網——每日最新資訊28at.com

scheduler.yield

我們需要注意一下,交出主線程控制權并不是 setTimeout 的設計目標,它的核心目標是能在未來某個時間完成某個任務,所以它會把任務中的工作排在隊列的最后面。Ljl28資訊網——每日最新資訊28at.com

但是,與之相反,默認情況下,scheduler.yield 會將剩余的工作發送到隊列的前面。這意味著你想要在 yield 后立即恢復的工作不會讓位于其他來源的任務(用戶交互除外)。Ljl28資訊網——每日最新資訊28at.com

scheduler.yield 是一個向主線程主動屈服并在調用時返回 Promise 的函數。這意味著你可以在異步函數中等待它:Ljl28資訊網——每日最新資訊28at.com

async function yieldy () {  // Do some work...  // ...  // Yield!  await scheduler.yield();  // Do some more work...  // ...}

還是使用前面的例子,這次我們使用 scheduler.yield 進行等待:Ljl28資訊網——每日最新資訊28at.com

async function runTaskQueueSchedulerDotYield () {  if (typeof intervalId === "undefined") {    alert("Click the button to run blocking tasks periodically first.");        return;  }  if ("scheduler" in window && "yield" in scheduler) {    clearTaskLog();    for (const item of [1, 2, 3, 4, 5]) {      blockingTask();      logTask(`Processing loop item ${item}`);      await scheduler.yield();    }  } else {    alert("scheduler.yield isn't available in this browser :(");  }}

我們會發現打印的結果是這樣的:Ljl28資訊網——每日最新資訊28at.com

Processing loop item 1Processing loop item 2Processing loop item 3Processing loop item 4Processing loop item 5Ran blocking task via setIntervalRan blocking task via setIntervalRan blocking task via setIntervalRan blocking task via setIntervalRan blocking task via setInterval

這樣就可以達到兩全其美的效果:既能將長任務進行分割,主動給主線程讓出控制權來提高網站的交互響應速度,又能確保讓出主線程后要完成的工作不會被延遲。Ljl28資訊網——每日最新資訊28at.com

試用

如果大家對 Scheduler.yield 感興趣并且想嘗試一下,從 Chrome 115版本開始可以:Ljl28資訊網——每日最新資訊28at.com

打開 chrome://flags ,然后選擇啟用 Experimental Web Platform Features ,這樣就可以使用 Scheduler.yield 了。也可以嘗試使用官方提供的 Polifill :https://github.com/GoogleChromeLabs/scheduler-polyfillLjl28資訊網——每日最新資訊28at.com

如果在業務代碼里使用,為了兼容不支持的低版本瀏覽器,可以在不支持時回退到 setTimeout 寫法:Ljl28資訊網——每日最新資訊28at.com

// A function for shimming scheduler.yield and setTimeout:function yieldToMain () {  // Use scheduler.yield if it exists:  if ('scheduler' in window && 'yield' in scheduler) {    return scheduler.yield();  }  // Fall back to setTimeout:  return new Promise(resolve => {    setTimeout(resolve, 0);  });}// Example usage:async function doWork () {  // Do some work:  // ...  await yieldToMain();  // Do some other work:  // ...}

當然,如果你不想讓你的任務被其他任務延遲掉,也可以在不支持這個 API 時選擇不屈服:Ljl28資訊網——每日最新資訊28at.com

// A function for shimming scheduler.yield with no fallback:function yieldToMain () {  // Use scheduler.yield if it exists:  if ('scheduler' in window && 'yield' in scheduler) {    return scheduler.yield();  }  // Fall back to nothing:  return;}// Example usage:async function doWork () {  // Do some work:  // ...  await yieldToMain();  // Do some other work:  // ...}

本文鏈接:http://www.www897cc.com/showinfo-26-71932-0.html瀏覽器也擁有了原生的 “時間切片” 能力!

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 性能優化例子:使用Performance工具分析性能瓶頸,解決頁面卡頓!

下一篇: C++ 慣用法之 Policy-based design

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 云安县| 温泉县| 黔南| 铜陵市| 平阴县| 宁强县| 海伦市| 宣威市| 长治县| 禹州市| 永济市| 博客| 濮阳县| 阿坝| 大庆市| 米易县| 新野县| 中卫市| 桦川县| 襄樊市| 观塘区| 长乐市| 临城县| 黑山县| 台东市| 同江市| 车致| 比如县| 乡城县| 潜江市| 九台市| 繁峙县| 久治县| 恭城| 四子王旗| 铁力市| 泌阳县| 浠水县| 锦屏县| 青龙| 治县。|