一、啥子是js中的事件循环
js是一门单线程
的非阻塞
的语言,当js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列
。被放入事件队列中的任务并不会立即执行,等到当前执行栈中的所有任务都执行完毕后,主线程才会依次执行任务队列中的任务。如此循环往复,直至所有的代码都执行完毕。这就是js中的事件循环
机制。如下图所示。
图中的Web API我们可以理解为一些异步事件,任务队列中的异步任务可以理解为这些异步事件返回的结果。图中右侧的微任务/宏任务队列我们先选择性忽略,等下再解释。
二、事件循环中的任务队列
上图中的微任务队列和宏任务队列其实是任务队列的进一步细分,那为什么要把一个好好的任务队列一分为二呢?因为js中的异步任务分为两类:微任务
和宏任务
,并且微任务的执行优先级要高于宏任务的,所以才需要使用微任务队列和宏任务队列来将二者区分开。于是,我们就可以顺理成章地推测(当然,事实上也是如此)在同一次事件循环中,主线程只有在执行完微任务队列里的任务后才会去执行宏任务队列里的任务。
三、第二点的逻辑不对吧?
可能有些小伙伴在看完第二点之后会产生两个疑问:
1、宏任务和微任务是o_O??
2、凭啥微任务的优先级比宏任务的高,XXX表示不服。
首先,关于微任务和宏任务是什么(浏览器环境下)。
总的来说,我认为宏任务是由宿主(浏览器)
发起的异步请求的处理结果;微任务是由js引擎
发起的异步请求的处理结果。具体的栗子有:
微任务:
- Promise
- async/await
宏任务:
- setTimeout / setInterval
- Ajax
- 整体script代码
其次,微任务的优先级确实是比宏任务高的(亲测)。
如果有疑问,可以用下面的代码去测试。
原理:以DOM渲染的时机作为参照;其次,利用alert() 能中断js的执行和DOM渲染的特点来观察
<!--html代码-->
<div style="color: orange;">DOM渲染了</div>
//js代码
<script>
setTimeout(()=>{
alert(`宏任务执行`);
},0)
Promise.resolve(`微任务执行`).then(res => alert(res))
</script>
测试的结果为:
- 浏览器先弹窗输出
微任务执行
,并且此时页面空白 - 页面渲染,显示出
DOM渲染了
- 浏览器再弹窗输出
宏任务执行
四、作个总结吧
基于上述几点,我们可以更详细地将js事件循环机制中的某次循环细分为。
- 执行栈中同步任务执行完毕
- 微任务执行(清空微任务队列)
- 渲染DOM
- 宏任务执行(清空宏任务队列)
- …进入下次循环…直至js代码执行完毕
至此,关于js事件循环的介绍就结束了,因为个人理解有限,如果文中有不严谨之处,还望大佬不吝赐教,在评论中留言(手动抱拳~)。