当前位置:首页 > 科技  > 软件

从简单中窥见高端,彻底搞懂任务可中断机制与任务插队机制

来源: 责编: 时间:2024-05-11 09:15:51 272观看
导读今天我就用最基础的方式重新跟大家分享一下什么是任务可中断。一、任务拆分首先,我们要明确的一个前提,是一个完整的函数执行是不可以中断的。因此如果你把一整个任务全部都放到一个函数中来执行,那么想要做到任务可中断

pP928资讯网——每日最新资讯28at.com

今天我就用最基础的方式重新跟大家分享一下什么是任务可中断。pP928资讯网——每日最新资讯28at.com

一、任务拆分

首先,我们要明确的一个前提,是一个完整的函数执行是不可以中断的。因此如果你把一整个任务全部都放到一个函数中来执行,那么想要做到任务可中断是不可能的。pP928资讯网——每日最新资讯28at.com

例如,现在我有一个任务,往父级元素中插入 10 万个子节点 <span>1</span>,然后我们可以随便写这样一个函数来完成这个逻辑。pP928资讯网——每日最新资讯28at.com

btn.onclick = () => {  let i = 0  for (; i < 100000; i++) {    let span = document.createElement('span')    span.innerText = 1    container.appendChild(span)  }}

然后这个时候,我们就发现一个问题,当我们点击之后,页面上并不会立即显示插入的内容,而是会卡顿一会儿,才会显示。pP928资讯网——每日最新资讯28at.com

pP928资讯网——每日最新资讯28at.com

原因是因为 10 万个节点的插入逻辑是一个同步的过程,JS 逻辑的执行时间过长导致了浏览器迟迟无法执行渲染。pP928资讯网——每日最新资讯28at.com

那么为了优化这种情况,我们可以考虑将渲染 10 万个节点这个大的任务,拆分成 10 万个渲染 1 个节点的小任务。pP928资讯网——每日最新资讯28at.com

function task() {  let span = document.createElement('span')  span.innerText = 1  container.appendChild(span)}

并将这 10 万个任务,放进一个数组中。pP928资讯网——每日最新资讯28at.com

const taskQueue = Array.from({ length: 100000 }, () => task)

执行这 100 万个任务,就通过遍历 taskQueue 的方式来执行,这样,我们就可以通过中断队列遍历的方式,来中断任务的执行。pP928资讯网——每日最新资讯28at.com

二、需要中断的原因

在浏览器中,渲染引擎在每一帧都有机会渲染页面,那么页面的表现就不会卡顿。但是刚才我们的情况是,JS 执行时间过长,导致渲染引擎一直没有机会渲染,所以用户感受到的就是卡顿。pP928资讯网——每日最新资讯28at.com

那么解决这个问题的原理,就是根据浏览器渲染频率,对 JS 要执行的任务进行拆分,JS 执行一部分,然后渲染引擎渲染一部分,完成之后,JS 再继续执行,渲染引擎再渲染。pP928资讯网——每日最新资讯28at.com

通过这样间隔执行的方式,让用户感知不到卡顿的存在。pP928资讯网——每日最新资讯28at.com

pP928资讯网——每日最新资讯28at.com

此时我们为了更好的观察效果,让每一个小任务的执行都阻塞 1ms。pP928资讯网——每日最新资讯28at.com

function task() {  const startTime = performance.now()  let span = document.createElement('span')  span.innerText = 1  while (performance.now() - startTime < 1) {    // 阻塞 1 ms  }  container.appendChild(span)}

然后把任务数量改成 1000。pP928资讯网——每日最新资讯28at.com

const taskQueue = Array.from({ length: 1000 }, () => task)

执行效果如下:pP928资讯网——每日最新资讯28at.com

pP928资讯网——每日最新资讯28at.com

五、插队

我们另外起一个按钮,专门用于执行一些插队任务。插队的逻辑非常简单,只需要往 taskQueue 中添加任务即可。不过插队任务的优先级更高一些,因此要通过 push 来添加,以确保任务能够更早的执行。pP928资讯网——每日最新资讯28at.com

首先声明一个 highPriorityTask 函数用于创建优先级更高的任务。pP928资讯网——每日最新资讯28at.com

function highPriorityTask() {  const startTime = performance.now()  let span = document.createElement('span')  span.style.color = 'red'  span.innerHTML = '<strong>插队任务</strong>'  while (performance.now() - startTime < 1) {    // 阻塞 1 ms  }  container.appendChild(span)}

新增一个按钮,用于触发插队任务的执行。pP928资讯网——每日最新资讯28at.com

pushBtn.onclick = function () {  taskQueue.push(highPriorityTask)}

我们来看一下执行效果,每当我点击插队任务按钮,就会执行一个优先级更高的任务。pP928资讯网——每日最新资讯28at.com

pP928资讯网——每日最新资讯28at.com

代码非常的简单,不过理解可能需要稍微思考一下。因为 performWorkUnit 中递归在遍历队列 taskQueue,并且这个递归过程是一直处于中断 -> 恢复的过程中,因此,当遍历被中断后,在它恢复之前,我们可以往 taskQueue 中插入新的任务到队列头部,当它重新开始遍历时,新加入的任务就会被执行。pP928资讯网——每日最新资讯28at.com

这里一个小的细节是,在事件循环的运行规则中,点击事件的回调会比 requestIdleCallback 更早执行。pP928资讯网——每日最新资讯28at.com

六、总结

这个逻辑就是 React 并发模式的底层原理。只不过在 React 中,同时兼容了同步更新与异步更新,并且设计了更加复杂的优先级机制,增加了更多场景的条件判断,导致源码看上去变得更加复杂了。pP928资讯网——每日最新资讯28at.com

当然,React 由于为了兼容更多的场景,改写了任务中断的判断条件。因为在别的环境里,例如 node/React Native 等,不支持 requestIdleCallback,在这些场景之下,React 把中断策略改为 5ms 中断一次,然后利用 performance.now 或者 Date.now 来记录时间。pP928资讯网——每日最新资讯28at.com

/* eslint-disable no-var */var getCurrentTime;var hasPerformanceNow = typeof performance === 'object' && typeof performance.now === 'function';if (hasPerformanceNow) {  var localPerformance = performance;  getCurrentTime = function () {    return localPerformance.now();  };} else {  var localDate = Date;  var initialTime = localDate.now();  getCurrentTime = function () {    return localDate.now() - initialTime;  };
function shouldYieldToHost() {  var timeElapsed = getCurrentTime() - startTime;  if (timeElapsed < frameInterval) { // 5ms    // 主线程只被阻塞了很短时间;    // smaller than a single frame. Don't yield yet.    return false;  }   // 主线程被阻塞的时间不可忽视  return true;}

并使用别的方式来替代 requestIdleCallback。pP928资讯网——每日最新资讯28at.com

  • node/old IE:setImmediate
  • DOM/worker:MessageChannel
  • 兜底方案:setTimeout
let schedulePerformWorkUntilDeadline;if (typeof localSetImmediate === 'function') {  // Node.js and old IE.  schedulePerformWorkUntilDeadline = () => {    localSetImmediate(performWorkUntilDeadline);  };} else if (typeof MessageChannel !== 'undefined') {  // DOM and Worker environments.  // We prefer MessageChannel because of the 4ms setTimeout clamping.  const channel = new MessageChannel();  const port = channel.port2;  channel.port1.onmessage = performWorkUntilDeadline;  schedulePerformWorkUntilDeadline = () => {    port.postMessage(null);  };} else {  // We should only fallback here in non-browser environments.  schedulePerformWorkUntilDeadline = () => {    // $FlowFixMe[not-a-function] nullable value    // @ts-ignore    localSetTimeout(performWorkUntilDeadline, 0);  };}

本文链接:http://www.28at.com/showinfo-26-87957-0.html从简单中窥见高端,彻底搞懂任务可中断机制与任务插队机制

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 有哪些 Java 面试题 90% 的公司否会问到?

下一篇: RecyclerView中ItemDecoration的精妙用法,实现自定义分隔线、边距和背景效果

标签:
  • 热门焦点
  • MIX Fold3包装盒泄露 新机本月登场

    小米的全新折叠屏旗舰MIX Fold3将于本月发布,近日该机的真机包装盒在网上泄露。从图上来看,新的MIX Fold3包装盒在外观设计方面延续了之前的方案,变化不大,这也是目前小米旗舰
  • K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • 让我们一起聊聊文件的操作

    文件【1】文件是什么?文件是保存数据的地方,是数据源的一种,比如大家经常使用的word文档、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存数据,它既可以保
  • 微信语音大揭秘:为什么禁止转发?

    大家好,我是你们的小米。今天,我要和大家聊一个有趣的话题:为什么微信语音不可以转发?这是一个我们经常在日常使用中遇到的问题,也是一个让很多人好奇的问题。让我们一起来揭开这
  • JVM优化:实战OutOfMemoryError异常

    一、Java堆溢出堆内存中主要存放对象、数组等,只要不断地创建这些对象,并且保证 GC Roots 到对象之间有可达路径来避免垃 圾收集回收机制清除这些对象,当这些对象所占空间超过
  • 花7万退货退款无门:谁在纵容淘宝珠宝商家造假?

    来源:极点商业作者:杨铭在淘宝购买珠宝玉石后,因为保证金不够赔付,店铺关闭,退货退款难、维权无门的比比皆是。&ldquo;提供相关产品鉴定证书,支持全国复检,可以30天无理由退换货。&
  • 品牌洞察丨服务本地,美团直播成效几何?

    来源:17PR7月11日,美团App首页推荐位出现&ldquo;美团直播&rdquo;的固定入口。在直播聚合页面,外卖&ldquo;神枪手&rdquo;直播间、美团旅行直播间、美团买菜直播间等均已上线,同时
  • 华为Mate60标准版细节曝光:经典星环相机模组回归

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • OPPO K11搭载高性能石墨散热系统:旗舰同款 性能凉爽释放

    日前OPPO官方宣布,将于7月25日14:30举办新品发布会,届时全新的OPPO K11将正式与大家见面,将主打旗舰影像,和同档位竞品相比,其最大的卖点就是将配备索尼
Top