>>1.前要
无聊无聊好无聊,感觉上班很无聊,学习很无聊,什么都很无聊,反正时间浪费也是浪费,不如水篇文章算了,里面的理解可能有误,希望大家不要因为我是一朵娇花而怜惜我,狠狠的批判我吧 ♂
>>2.为什么要有事件循环?
首先先来谈谈JS的运行机制,JS是单线程大家都没有意见吧?那就意味着会遇到单线程阻塞的问题。什么你问我什么是单线程阻塞问题?
就好比排队去买东西,你买了瓶矿泉水,你前面那个家伙买了份现炸的炸鸡,要等10分钟才能炸好,而你必须等他结完账你才能结账,那这个买炸鸡的家伙不仅阻塞了你还把整条队伍都阻塞了10分钟,这个时候超市经理出来了,看到队伍10分钟都没动了,这怎么得了,赶紧问超市服务员咋回事,了解了问题后才发现原来是买炸鸡这小子阻塞了队伍,这哪行啊,这太影响我流水了,得想个办法解决这个问题。
和上面买炸鸡阻塞队伍的问题一样,JS在执行的时候也会有阻塞问题,举一个简单的例子,请求接口的时候,如果没有事件循环的话,那JS就得一直等着接口返回后执行完接口的回调函数才能执行后面的程序,那这个接口也就阻塞了整个线程。
>>3.事件循环如何解决阻塞问题
了解了事件循环是为了避免阻塞问题的之后,我们来了解一下事件循环是如何解决阻塞问题的,毕竟影响了超市流水可不行。
超市经理在一段冥思苦想之后,终于想到了解决方案,要么让买炸鸡的滚蛋,要么让买炸鸡站旁边等着,等炸鸡好了再结账,为了利益最大化经理选择了后者,经理专门划分了一块区域给买炸鸡和其他炸货的人,并给了他们号码牌,等东西炸好了叫号上来选择加什么调料,要是同时2个被叫到了号,就按叫号的先后顺序排队。等当前正在购物的人算完账后,再去帮买炸鸡的人进行调料。这样既不会影响炸鸡喜好者的购物体验,也不会因为阻塞队伍而影响超市流水,哈哈哈我超市经理真是一个天才!
买炸鸡后加调料的行为可以理解为调用接口后的回调函数,等接口返回到了,JS服务员也会根据接口进行叫号,将对应的回调任务推入微任务队列,等当前的宏任务执行完后(等当前正在购物的人算完账后),查看微任务队列中是否有需要执行的,有的话就执行微任务队列中的任务,执行完后再执行下一个宏任务。这样就解决了阻塞问题。下面我们来正式的了解一下什么是事件循环,什么是宏任务微任务。
>>4.什么是宏任务、微任务
在了解事件循环之前,我们先了解一下什么是宏任务和微任务。
在JavaScript中,宏任务(Macrotask)和微任务(Microtask)是指不同类型的异步任务。
常见的宏任务:
- 定时器任务(如
setTimeout和setInterval)- DOM 事件(如点击事件、输入事件等)
- AJAX 请求的回调函数
- 资源加载的回调函数(如图片或脚本加载完成后的回调)
- script (整体代码)
宏任务会被添加到宏任务队列(也称为任务队列或消息队列)中,主线程执行完当前的同步任务后,会从宏任务队列中取出一个宏任务执行。
这里说一下为什么setTimeout不是微任务,虽然setTimeout也是异步任务,但是setTimeout的回调函数是添加到宏任务队列中等待执行,优先级低于微任务,这一点需要注意。
常见的微任务:
- Promise.then
- process.nextTick (node.js)
MutationObserver的回调函数微任务会被添加到微任务队列中,在当前宏任务执行完毕后立即执行,也就是说微任务会在下一个宏任务之前执行。如果一个宏任务中产生了多个微任务,那么这些微任务会按照添加顺序依次执行。
这里需要注意一下process.nextTick,它是nodejs中的方法,会把回调函数放在当前执行栈的底部,会优先于其他微任务执行。
>>5.什么是事件循环
了解完宏任务、微任务后,我们来了解一下事件循环。
事件循环(Event Loop)是浏览器或其他执行环境用来处理异步操作的一种机制。它负责协调和调度任务的执行,包括处理用户交互事件、定时器事件、网络请求等异步操作(等买炸鸡操作)。
事件循环的基本原则是,将任务分为两类:宏任务(Macrotask)和微任务(Microtask,优先级高于宏任务)。
事件循环的执行过程如下:
- 执行一个宏任务(如执行整体代码、处理用户交互事件等)。
- 检查是否有微任务队列,如果有,则依次执行微任务,直到微任务队列为空。
- 更新渲染,如果有需要渲染的内容。
- 执行下一个宏任务,重复上述步骤。
通过事件循环机制,可以保证异步任务的执行顺序和协调性,避免阻塞主线程,提供更好的用户体验(提高超市流水)。
>>6.总结
此文介绍了:
- 什么是微任务、宏任务。
- 什么是事件循环,以及事件循环的机制。
学习完了后,大家做做题目试试吧。
// nodejs环境下
console.log(1)
new Promise((reslove,reject) => {
console.log(2)
reslove('')
}).then(res => {
console.log(3)
})
console.log(4)
process.nextTick(() => {
console.log(5)
})
console.log(6)
setTimeout(() => {
console.log(7)
},0)
// 打印
// 1 同步任务不解释
// 2 promise的构造不是异步的,此时.then推入微任务队列
// 4 同步任务不解释
// 6 同步任务不解释
// 5 process.nextTick 的回调优先于其他微任务执行,置于当前执行栈底部
// 3 .then 在宏任务执行完后执行,但是被nextTick插队了
// 7 setTimeout的回调被推入下一个宏任务,等微任务执行完后开始执行
>>7.寄语
感谢大家读完此文,本人才疏学浅,有错误的地方希望大家指正。希望大家不要因为我是一朵娇花而怜惜我,狠狠的批判我吧 ♂