postTask:React的杀手锏被浏览器原生实现了?
source link: https://segmentfault.com/a/1190000040751671
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
大家好,我卡颂。
React
这几年一直在完善的并发模式主要由以下两部分组成:
- 基于
fiber
实现的可中断更新的架构 - 基于调度器的优先级调度
可以说,从16年开始重构fiber
架构到今年底(或明年初)React18
发布正式版,这期间React
团队大部分工作都是围绕这两点展开的。
如果现在告诉你,React
呕心沥血多年实现的优先级调度,浏览器原生就支持,会不会很惊讶?
文章参考Building a Faster Web Experience with the postTask Scheduler。
什么是优先级调度
假设,我们有个记录日志的脚本需要在页面初始化后执行:
initCriticalTracking();
调用栈火炬图如下:
可以看到,这是个执行了249.08ms的长任务,在执行期间浏览器会掉帧(表现为:浏览器卡顿)。
现在,我们将其包裹在优先级调度函数scheduler.postTask的回调函数中:
scheduler.postTask(() => initCriticalTracking());
长任务被分解为多个短任务:
在每个任务之间浏览器有机会重排、重绘,减少了掉帧的可能性。
这种根据任务优先级将任务拆解,分配执行时间的技术,就是优先级调度。
scheduler.postTask是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上,所以我们可以取消尚在排队还未执行的回调函数。
通过使用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
现在在做的事么?
欢迎加入人类高质量前端框架研究群,带飞
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK