前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >dom更新到底在javascript事件循环的哪个阶段?「前端每日一题v22.11.17」

dom更新到底在javascript事件循环的哪个阶段?「前端每日一题v22.11.17」

作者头像
FE情报局
发布2022-12-05 10:21:13
7780
发布2022-12-05 10:21:13
举报
文章被收录于专栏:FE情报局

dom更新到底在javascript事件循环的哪个阶段?「前端每日一题v22.11.17」

昨天写了一篇文章,是javascript的事件循环机制,然后在某乎上也发了,在发的时候看到了一个问题,dom渲染在事件循环的哪个阶段?

看到这个问题的时候,我冷然一笑,这不是明显着么?肯定是在事件循环中的异步任务队列,任务队列又分为宏任务和微任务,dom更新在微任务队列清空之后,宏任务队列开始之前。

结论大家都知道,但是任何事情都要有实践,实践是检验真理的唯一标准

验证

如何验证呢?我写了以下的代码,在异步微任务和宏任务之间加一个dom的更新操作

代码语言:javascript
复制
setTimeout(() => {alert('暂停点alert');console.log('setTimeout done')}, 0)
document.getElementsByTagName('div')[0].innerHTML = 'FE情报局'
new Promise((resolve) => {resolve()}).then(() => {console.log('promise done')})

其中我在宏任务开始时加了一个alert,用来阻塞js,观测页面上是否已经有了「FE情报局」,当我满怀信心的按下的时候,页面上空空如也,我傻眼了

按下确认之后,页面上显示出来了

这就奇怪了,结论跟我预想的不一致!!!

查找原因

在我认为我发现了一个巨大的bug之后,然后疯狂搜集资料,发现所有结论都是dom更新确实是在微任务之后,那为什么表现不一致呢?

是不是浏览器没来得及更新?

于是在弹出alert的时候,我查看了一下dom元素

发现虽然页面上没有,但是dom元素已经正常的在DOM上了,这就涉及到另一个问题了,浏览器GUI线程的更新机制

UI线程和js线程

我们都知道,浏览器对于js的处理主要在js线程,页面渲染主要是在gui线程,由于js可以操作dom元素,所以js是会影响页面渲染的,也就是会影响gui线程的渲染结果。这样这两个线程就不能同时工作,一旦同时工作,相互影响就会造成不可预估的影响。所以在浏览器中,js线程和gui线程是互斥的,只能允许一个线程进程任务的执行,js线程运行时,gui线程是不会运行的

有了这个基础,我们在讨论一下动画,这里涉及到一个概念,那就是刷新率

刷新率

我们平时也会经常听到刷新率这个词,比如我们电脑屏幕的刷新率达到了120赫兹,或者玩游戏的时候经常有一个词叫FPS,刷新率就是指显示器每秒绘制新图像的次数,60赫兹表示浏览器每秒绘制60次,由于人眼的暂留效果,我们就可以看到流畅的动画了

如果1秒你的刷新率只有10,你就会发现自己看的动画跟ppt一样,十分卡顿

当前主流的浏览器刷新频率为60赫兹,也就是说刷新一次所需要的时间是1000/60=16.6毫秒,根据UI线程和js线程互斥的关系,我们可以理解为浏览器要在这16.6毫秒以内完成js脚本和浏览器渲染

回到正题

到这里就很好理解了,我们在刚开始执行那段js代码的时候,虽然在元素下看dom已经更新到dom树上了,但是浏览器并没有刷新,所以本质上还是没有展示出来,但是我一直觉得是因为alert的原因导致的,所以我采用了另一种方式

代码语言:javascript
复制
setTimeout(() => {
  for (let index = 0; index < 1000000; index++) {
    if(index===1000000-1){
      console.log('end')
    }
  }
  console.log('setTimeout done')
},0)
document.getElementsByTagName('div')[0].innerHTML = 'FE情报局'
new Promise((resolve) => {resolve()}).then(() => {
  console.log('promise done')
  for (let index = 0; index < 1000000; index++) {
    if(index===1000000-1){
      console.log('done')
    }
  }
})

这个时候就会发现dom在进入到setTimeout之前,在done之后就已经渲染到浏览器上了,刚开始直接显示出来一部分的原因是因为alert导致的,换成正常的阻塞流程的js就可以了

欢迎大家留言讨论,是不是因为alert的机制导致的在微任务结束之后,宏任务中的alert阻碍了dom的渲染,导致UI线程并未能够及时刷新

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FE情报局 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 验证
  • 查找原因
  • UI线程和js线程
  • 刷新率
  • 回到正题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档