面试官:你真的理解 Event Loop 吗?( JS 事件循环 )

945 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

写在前面

最近看了下小伙伴的代码,发现一些小白经常会因为无法判断代码执行的时机而出现了一些莫名其妙的 Bug ,比如在使用 Vue 的过程中,经常因为 Dom 没加载完执行了 Dom 读取操作,导致了读取到的 Dom 元素为空的问题,这个时候小伙伴们经常就是盲猜,加个 this.$nextTick() 或者 setTimeout 来尝试解决问题, 发现现象正常了,就万事大吉,又拿起了新的砖头,开始搬砖。这篇文章将通过对 JS 的事件循环进行学习,总结,让我们在下次重新遇到相关问题的时候,能够深入原理,从原理上理解 Bug 的原因,然后以正确的姿势解决 Bug,让这种 Bug 无处遁形!

JS 的单线程与异步

我们都知道无论是在浏览器环境下还是在 Nodejs 环境下,JS 的执行都是单线程的,在浏览器中,JS 的执行与 Dom 的渲染也是共用一个线程的,那么这就会出现一个问题,我们的程序会因为所有代码都同步执行出现某个任务被阻塞,出现“假死”状态,那么异步便是这些问题的解决方案,通过异步的方式,能够使代码不被阻塞,一旦出现需要等待的任务,将会放到队列里,等待任务完成后返回,而不是强制阻塞其他任务的执行

什么是异步

异步的分类

异步主要分为宏任务和微任务,宏任务主要包括 setTimeout、setInterval、ajax,而微任务则包括 promise、async、await

异步的执行时机

微任务的执行主要在下一轮 Dom 渲染之前执行,而宏任务在下一轮 Dom 渲染之后执行

异步执行代码演示

为了探究宏任务和微任务的执行时机,我们分别以 setTimeout 作为宏任务代表以及 promise 作为微任务代表,并在 JS 中进行 Dom 操作后执行,观察微任务和宏任务执行时 Dom 的渲染情况(即页面呈现出来的样子),需要注意的是,JS 中的 Dom 对象完成操作并不等于渲染完成。

<body>
  <script>
    // js 对 Dom 的操作
    const mynameDiv = document.createElement('div')
    mynameDiv.innerText = 'Yimwu'
    document.body.appendChild(mynameDiv)
    // EventLoop 测试
    console.info('testStart')
    Promise.resolve().then(() => {
      alert('Promise Alert')
    })
    setTimeout(() => {
      alert('Settimeout Alert')
    });
    console.info('testEnd')
  </script>
</body>

GIF 2022-6-5 1-01-39.gif

实验结果可以看出,当触发 Promise 的 alert 时,页面上并没有看到 Yimwu的字样,也就是 Dom 渲染在这个时候并没有完成,而当 Settimeout 的 alert 触发时,出现了Yimwu的字样,即 Dom 渲染已完成

Event Loop

image.png 如上图所示,理解了异步之后,Event Loop 就比较清晰了,顾名思义,事件循环,就是监听队列,按顺序执行任务的机制。在异步中我们了解到异步一共分为微任务和宏任务,其实微任务和宏任务在 Event Loop 中加入的是不同的两个队列。在 JS 中当执行完同步代码后,会将微任务加入到微任务队列(Microtask Queue),将宏任务加入到宏任务队列(Macrotask Queue),启动监听,持续监听并按队列执行微任务和宏任务,而微任务和宏任务的执行顺序在异步中已经做了详细的解释,即 微任务 --> Dom 渲染 --> 宏任务

关于任务添加到队列里的时机可以这样子理解,以 settimeout 为例,当程序执行到 settimeout 时,将会在 settimeout 时间走完后,将任务加入宏任务队列,等待执行。

到这里,勤于思考的小伙伴可能会发现一个问题,如果当 settimeout 时间走完后,将任务加入宏任务队列里,但是队列里还有很多或者耗时非常大的任务在执行,那岂不是需要继续等待,那任务真正执行的时间也就和 settimeout 的时间不匹配了呀????对的,确实如此,如果遇到真正极端的状况,那么确实会发生不匹配的问题,所以这可能也是 settimeout 一直被人诟病的原因之一吧~~

参考文章(致谢)

1、js事件循环

2、双月老师前端面试题

往期好文推荐

面试官:说说从输入 URL 到页面显示到底经历了什么,体现一下你的知识广度

面试官:作为前端,服务器相关了解多少?

面试官:HTTPS 采用的是对称加密还是非对称加密?具体说说其加密过程

面试官:说说 Cookie 和 Token 的区别?

面试官:网络安全了解多少,简单说说?(一)

面试官:网络安全了解多少,简单说说?(二)

面试官:网络安全了解多少,简单说说?(三)

面试官:网络安全了解多少,简单说说?(四)

面试官:网络安全了解多少,简单说说?(五)

面试官:网络安全了解多少,简单说说?(六)

面试官:网络安全了解多少,简单说说?(七)

面试官:网络安全了解多少,简单说说?(八)

浅尝 | 从 0 到 1 Vue 组件库封装

面试官:这么简单的正则表达式都不会?

Webpack 打包类库踩坑

面试官:你就只会 npm run build 吗?(Webpack 配置 Vue+Ts)

面试官:连VuePress都没搭过还说开发过组件库?(VuePress 搭建)

面试官: 连 Vue 视图更新都不会写?(Vue视图更新原理【一】)

面试官: 能不能手写 Vue 响应式?(Vue2 响应式原理【完整版】)

面试官:能不能手写 Vue3 响应式(Vue3 原理解析之响应系统的实现)

JS 优雅之道(JS 代码优化小 Tip)

面试官:你真的会用 SVG 吗? (SVG 应用实战)

面试官:说一下这个Loading动画实现思路 (CSS3 实现 Loading 动画)

JS 扫盲题 ( 面试题梳理系列 (一))

面试官:你确定你说的防抖不是节流吗?( 面试题梳理系列 (二))

面试官:除了 HTTP,你还用过什么通信协议?(Websocket 在数字孪生中的应用)

面试官:你真的理解 Event Loop 吗?( JS 事件循环 )

面试官:v-for 中 key 为什么不能用 index,从原理层面聊聊?

面试官:vue-router 的 hash 与 history 哪个模式会刷新页面?

面试官:说说你平时用过的自适应方案(数字孪生可视化自适应方案)

面试官:说一下如何优化过渡动画(数字孪生可视化过渡动画)

写在最后

博主接下来将持续更新好文,欢迎关注博主哟!!
如果文章对您有帮助麻烦亲点赞、收藏 + 关注和博主一起成长哟!!❤️❤️❤️