页面运行中事件频繁触发会阻塞页面吗?

简介: 之前看`防抖`和`节流`的时候,看到短时间内大量的事件触发会引起浏览器卡死,浪费浏览器性能,那么为什么事件触发会引起阻塞页面的情况呢?引起页面阻塞的原因真的是因为事件触发太多了吗?

之前看防抖节流的时候,看到短时间内大量的事件触发会引起浏览器卡死,浪费浏览器性能,那么为什么事件触发会引起阻塞页面的情况呢?引起页面阻塞的原因真的是因为事件触发太多了吗?

注:本篇文章测试用例来源于 Chrome

怎样算卡?

页面运行阶段怎样算是?如果以用户的角度来看,那点击一个按钮没响应,算卡;下拉页面没动,算卡;图片停止加载,GIF 图不转了,动画停止了,算卡;总之,就是页面无法做出用户认为正确的动作就算卡,此时在本篇文章中认为是页面被阻塞,那么事件的高强度触发会不会导致页面的呢?

交互失效

关于浏览器运行这块, JS 线程是和 GUI 渲染线程是互斥,我们通常说的页面卡死,就是不动了,没响应了,通常可能就是 JS 线程在高强度占用中,而为什么说,事件高频触发会影响到浏览器卡死,是因为 Event Loop 机制,事件触发后会往任务队列中的塞入任务,就是像点击事件的回调这种,然后 JS 引擎会读取任务队列,然后执行 JS, 这个时候如果说你的 JS 执行时间很久,那么就会阻塞页面的渲染,那么高频触发的话就会一直阻塞页面,那么为了证明上面的猜想(目前是),就去试试看

首先验证,点击按钮没响应这块,这方面对应的是 JS 无法及时执行,监听 mouseenter 事件(因为点击事件在动图不太能看出来),和以一个空转的 for 循环来模拟 JS 高强度运算的情况,代码如下

<!DOCTYPE html>
<html>
  <body>
    <div onmouseenter="add()"></div>
  </body>
  <script>
    // 9 个 0
    // 点击一次会打印一次
    let num = 0;
    function add() {
      console.log(++num);
      for (let i = 0; i < 3000000000; i++) {}
    }
  </script>
  <style>
    div {
      width: 100px;
      height: 100px;
      background-color: black;
    }
  </style>
</html>

结果如下

1.gif

mouseenter 事件触发了三次,就是鼠标移进去 div 3 次,第一次马上打印出来了,第二次等了一下才打印,这说明 JS 的执行的确会阻塞页面,但是,这是交互上的阻塞,导致 JS 延迟执行,因为 JS 线程在第二次事件触发时还在执行第一次的 JS, 注意,有小细节,这里提到了 线程,其实刚才页面上还有一个线程,那就是事件触发线程,从刚才一个打印了 3 次而不是 1 次或者两次,可以说明事件触发线程它是一个独立的线程,类似下载线程,不是和 JS 线程互斥的,当我们 JS 线程在执行的时候,事件也能够正常的被触发

又是一个小细节

这里也回一下主题,就是事件高频触发会不会阻塞页面,从这种情况来看,因为事件触发线程的独立,所以 JS 线程会不断执行被任务队列中的任务,如果恰巧这个任务是计算复杂型的 JS, 要花很长时间,比如点击一个按钮后没响应再多次点击,就会造成 JS 延迟执行,最终导致整个页面的交互失效

这种情况可以考虑使用 web worker 再开一个线程的方式去处理,感兴趣的去百度

动画类阻塞

JS 延迟执行算是交互类型的一种阻塞,那么 JS 线程是和 GUI 线程互斥, 事件的高频率触发,也必然会不断执行 JS, 那么类似 GIF 和 动画这种涉及 GUI 线程的会不会被阻塞,也就是证明 JS 线程和 GUI 线程互斥,还是以上面的例子为模板,加上一个 img 动图

<!-- ... -->
<div onmouseenter="add()"></div>
<p>---分割线---</p>
<img src="http://localhost:8000/gif.gif" />
<!-- ... -->

结果如下

2.gif

这个结果我还是有点意外的,因为我之前看过一篇文章曾经说过 JS 执行会阻塞动图的加载,现在实践看来并不是真的,难道是我们用的浏览器不一样?至少 Chrome 的计算复杂型的 JS 并不会阻塞动图的加载

那么排除掉事件触发对动图加载的影响,接下来将看一下 CSS 动画会不会受到 JS 的影响,还是以上面的例子为模板,加上一个从左到右的动画

<!-- ... -->
<div onmouseenter="add()"></div>
<p>---分割线---</p>
<div class="animation"></div>
<!-- ... -->
<style>
  /* ... */

  @keyframes move {
    from {
      left: 0;
    }

    to {
      left: 100%;
    }
  }

  .animation {
    position: absolute;
    animation: move 5s linear infinite;
  }
</style>

3.gif

还是熟悉的 3 次 mouseenter 事件,我们设计的动画最终会等待 JS 的执行完成才能够继续进行,因此我们可以得出结论,在页面存在 CSS 动画的情况下,如果事件的高频触发并且触发的回调是计算复杂型的 JS, 会导致页面的阻塞(动画无法进行)

总结

  1. 事件的频繁触发并不是导致页面阻塞的真正原因,因为事件触发线程是独立线程
  2. 事件触发的回调的 JS 是否是计算复杂型,会不会是一个长任务才是阻塞页面的真正原因
相关文章
|
17天前
|
JavaScript
弹框下次触发时还保留上次的触发条件
弹框下次触发时还保留上次的触发条件
|
26天前
|
小程序 UED 开发者
小程序如何监听页面的滚动事件
小程序如何监听页面的滚动事件
21 0
|
3月前
|
JavaScript UED
常见的触发函数的事件(实现不同的用户体验)
常见的触发函数的事件(实现不同的用户体验)
17 0
|
2月前
|
JSON 前端开发 算法
2715. 执行可取消的延迟函数
2715. 执行可取消的延迟函数
13 0
|
6月前
|
消息中间件 架构师 数据处理
处理数据时点按钮,数据完数据时会自动执行的问题
处理数据时点按钮,数据完数据时会自动执行的问题
|
8月前
|
前端开发
页面多个请求时如何等所有请求完毕再执行某个动作?
页面多个请求时如何等所有请求完毕再执行某个动作?
44 0
|
8月前
|
JavaScript
JS 启动一个计时器来跟踪某一个操作的占用时长
JS 启动一个计时器来跟踪某一个操作的占用时长
39 0
|
9月前
|
Serverless 容器
每个请求通常会触发一个新的函数实例来处理
每个请求通常会触发一个新的函数实例来处理
51 1
|
JSON 小程序 JavaScript
【小程序】页面事件
【小程序】页面事件
123 0
【小程序】页面事件