PostTask:React的殺手锏被瀏覽器原生實現了?
大家好,我卡頌。
React這幾年一直在完善的「并發模式」主要由以下兩部分組成:
- 基于fiber實現的可中斷更新的架構
- 基于調度器的優先級調度
可以說,從16年開始重構fiber架構到今年底(或明年初)React18發布正式版,這期間React團隊大部分工作都是圍繞這兩點展開的。
如果現在告訴你,React嘔心瀝血多年實現的「優先級調度」,瀏覽器原生就支持,會不會很驚訝?
文章參考Building a Faster Web Experience with the postTask Scheduler[1]。
什么是優先級調度
假設,我們有個「記錄日志」的腳本需要在頁面初始化后執行:
- initCriticalTracking();
調用棧火炬圖如下:
可以看到,這是個執行了249.08ms的長任務,在執行期間瀏覽器會掉幀(表現為:瀏覽器卡頓)。
現在,我們將其包裹在「優先級調度函數scheduler.postTask」的回調函數中:
- scheduler.postTask(() => initCriticalTracking());
長任務被分解為多個短任務:
在每個任務之間瀏覽器有機會重排、重繪,減少了掉幀的可能性。
這種「根據任務優先級將任務拆解,分配執行時間的技術」,就是「優先級調度」。
scheduler.postTask[2]是Chrome實現的「優先級調度API」。
scheduler.postTask屬于試驗功能,需要在 chrome://flags 中打開 #enable-experimental-web-platform-features
之前是如何實現優先級調度的
在scheduler.postTask出現之前,通常使用瀏覽器提供的「會在不同階段調用的API」模擬「優先級調度」,比如:
- requestAnimationFrame(簡稱rAF)一般用來處理動畫,會在瀏覽器渲染前觸發
- requestIdleCallback(簡稱rIC)在每一幀沒有其他任務的空閑時間調用
- setTimeout、postMessage、MessageChannel在渲染之間觸發
React使用MessageChannel實現優先級調度,setTimeout作為降級方案。
但是,這些API畢竟都有本職工作。用他們實現的「優先級調度」比較粗糙。
基于此原因,postTask Scheduler誕生了。
postTask Scheduler的使用
scheduler.postTask有3種可選優先級:
使用方式很簡單,通過以下方式注冊的回調函數會以「默認優先級」調度:
- // 默認優先級
- scheduler.postTask(() => console.log('Hello, postTask'));
你也可以指定優先級與執行延遲:
- // 調用后延遲1秒執行,優先級最低
- scheduler.postTask(() => console.log('Hello, postTask'), {
- delay: 1000,
- priority: 'background',
- });
postTask建立在AbortSignal API[3]上,所以我們可以取消尚在排隊還未執行的回調函數。
通過使用TaskController API控制:
- const controller = new TaskController('background');
- window.addEventListener('beforeunload', () => controller.abort());
- scheduler.postTask(() => console.log('Hello, postTask'), {
- signal: controller.signal,
- });
同時,實驗性的schedule.wait方法可以讓我們輕松的等待某一時機后再執行任務。
比如,我們可以在頁面加載完成后異步加載xxx.js:
- async function loadxxx() {
- // 等待事件被派發
- await scheduler.wait('myPageHasLoaded');
- return import('xxx.js');
- }
- // 頁面加載后派發事件
- window.dispatchEvent(new CustomEvent('myPageHasLoaded'));
以上代碼被簡化為postTask的event配置項:
- scheduler.postTask(() => import('xxx.js'), {
- event: 'myPageHasLoaded'
- })
總結
「優先級調度」可以應用在很多領域,比如:
- 資源提前、延后請求
- 第三方資源延遲加載
- ......
可以預見,未來這勢必會增加前端編程復雜度。
就像曾經,當web應用復雜到一定程度時,出現了前端框架,開發者不用直接操作DOM。
未來,當「優先級調度」復雜到一定程度時,一定也會出現集成解決方案,讓開發者不用直接操作優先級。
慢著,這不就是React現在在做的事么?
參考資料
[1]Building a Faster Web Experience with the postTask Scheduler:
https://medium.com/airbnb-engineering/building-a-faster-web-experience-with-the-posttask-scheduler-276b83454e91[2]scheduler.postTask:
https://github.com/WICG/scheduling-apis/blob/main/explainers/prioritized-post-task.md[3]AbortSignal API:
https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal