
关于EventLoop
说起EventLoop(事件循环),这真是一个JS中经久不衰的话题啊,从我个人浅显的阅历来看,几乎每一个博主或者公众号作者的作品中都会有这样一篇关于事件循环讲解的,当然了,我也不例外,我也曾写下一篇见到异步就懵逼?带你认识JS Event Loop(事件循环)。其实关于EventLoop的知识,网上的文章铺天盖地,这篇文章我也不会重复去讲那些“略显基础”的知识点,同样,我还是用我个人的白话来总结一下,啥是EventLoop。
- JS在执行一段代码的时候分为同步任务和异步任务,其中同步任务会先执行,这一切都和浏览器的机制有关,这里不再赘述。
- 异步任务又分为微任务和宏任务,微任务会先于宏任务执行
当然了,两句话是不可能完全准确的概括的,大概就是这么个意思,如果再往深说点,那大概就是
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
如果你掌握了上面的知识,可能对于一些简单的问题和常见的业务逻辑有了一个处理手段。也算是比较实用,比如下面的一些代码
console.log('a') // 这里可能是一段实际业务
setTimeOut( () => {
console.log('b') // 这里可能是一段实际业务
}, 0)
我们通过上面的代码,对代码的执行顺序有了一个明确的控制。再或者,在react中,我们知道setState
是一个“伪异步”(只是看起来像异步,因为react的合成事件导致),所以我们经常会有setState
后拿不到最新状态的情况发生,当然了recat在setState
中是设有回调函数的,我们可以拿到,但是如果不用回调函数,那我们要如何做呢?这时候,也会用到事件循坏的东西。比如下面的代码
setState({
oldstate: newstate
})
setTimeOut( () => {
console.log(newstate) // 这样就可以拿到newState了,如果不加定时器,那就还是旧值
}, 0)
当然了,如果你了解了上面这些东西,平时开发应用是没问题了,可偏偏有一些“面试题”,让你抓狂,让你不知所措。绕来绕去的头晕,但往往,它能把你绕头晕,恰恰说明你对他的知识掌握的还不是很全面牢靠。下面,我用我个人出的一道题来更细致的说一下这个事件循环。
小编出题
Promise.resolve().then(() => {
console.log('p1')
setTimeout(() => {
console.log('t1')
Promise.resolve().then(() => {
console.log('p4')
})
}, 0)
})
setTimeout(() => {
console.log('t2')
Promise.resolve().then(() => {
console.log('p2')
setTimeout(() => {
console.log('t3')
}, 0)
})
Promise.resolve().then(() => {
console.log('p5')
})
}, 0)
求上面打印结果。
小编聊知识点
实际中,我们不会遇到使用这么复杂的异步逻辑去折磨自己,但如果你会这道题,那么关于事件循坏的大部分知识和题目,你都没有问题了。本文,我会假设你已经掌握了EventLoop的基本知识,那么我再补充一点。
- 当“宏任务”出队时,任务是一个一个执行的;而“微任务”出队时,任务是一队一队执行的。因此,我们处理“微任务”这一步,会逐个执行队列中的任务并把它出队,直到队列被清空。
- 事件循环结束的标志是直到两个队列都清空,在此之前,会一直执行循坏的过程。
讲到这里,其实就是给大家又划了两个重点,意在,不要再简单的说“同步先执行,异步后执行,微任务先执行,宏任务后执行”了。这样说是不全面的,也是存在一定的误解的,容易给你搞懵,比如上面这道题。在我补充完上面的知识点后,大家应该有一个疑问,什么是“一个一个”,什么是“一队一队”?这里我还是用自己的白话来说
- 一个一个,就是说,宏任务队列在执行的过程中,一个一个的清楚,遇到微任务拦截,就不执行了, 不管后面还有多少宏任务,没一个宏任务都是一个个体,我说的很啰嗦,但是相信你已经明白啥意思了
- 一队一队,就是说,微任务队列在执行过程中,只要一执行,就要一次性执行完。
好了,上面白话过后,那么我们直接看题吧,我来一步步分析
小编分析
- 同步任务先执行,上面代码中,被分出两个异步队列,一个“宏任务”,也就是
setTimeout
代码,一个“微任务”,也就是Promise
代码,我们知道,微任务会先执行,且会一对一对执行,也就是一次性把本次循环队列中的微任务全执行了,因为这里微任务只有他一个(在第一次循环中),说以就执行一下,打印p1,之后又遇到宏任务,且本次执行中,没有可执行的微任务,于是,宏任务队列出列,因为在宏任务队列中't2'先于't1',所以t2先执行。 - t2的
setTimeout
先执行,这时,打印t2,接着往下走,这时候遇到了微任务,会立即执行微任务,且把其中的微任务全部执行完毕,我们看一下,在这层循坏中,有两个微任务,所以会依次全部执行完,这时候,打印p2, p5,而在执行p2的时候,又有一个宏任务t3进入了宏任务队列,且排在t1后面。 - 当本次微任务执行完之后,这时候执行宏任务,一个一个先后执行,首先执行的是t1,这时候打印t1,而,执行t1的时候,又遇到的微任务,且只有一个,那么直接执行完这里所有的微任务,所以打印p4
- 微任务执行完后,又该去宏任务队列里去找,这时候轮到t3
- 走到t3打印完,两个队列都清空了,执行结束!所以打印顺序为
- p1, t2, p2, p5, t1, p4, t3,验证一下

讲到这里,我想大家明白这种复杂的事件循坏该怎么解决了吧?也应该对事件循坏有了一个更全面的了解了。好了!接下来直接看看吧!
网友评论