[技术分享]JavaScript 之 Event Loop

shangjin发布于1 个月前 • 61 次阅读

前言


大家都知道浏览器是单线程的,因为这个特性,我们必须了解浏览器事件执行机制,也就是event loop,以便写出高效能的代码!在开始正题前,我们必须先理解下几个概念。

  • 同步任务和异步任务

  • 堆和栈

  • 宏任务和微任务

同步任务和异步任务


个人的理解,同步任务是主线程需要执行的,也就是浏览器按照顺序执行的任务。异步任务则是保存在浏览器栈中的任务,需要某个时间段的主任务执行完毕,才会执行的任务。

堆和栈


程序的运行过程中产生了堆和栈,具体区别大家可以google,这里简单说下。在个人理解上,这2个都是内存,不同点是栈是系统自动分配释放,堆是程序员分配释放,也就是程序员能不能占有主导权。本文我们用到的是堆这个概念。

宏任务和微任务


这个网上版本很多,小编先来介绍,再来说说区别。通常来说,宏任务就是主线任务,主要执行的任务,微任务是基于每个宏任务,比如现在有3个宏任务,分别是A,B,C,和一个基于B任务的微任务D,那么浏览器的执行顺序是A,B,D,C,这个很好理解。我们看看微任务,主要有process.nextTick,MutationObserver,Promise.then catch finally,先把定时器搁置,下面买讨论。除了微任务的其余任务都是宏任务。

定时器是属于宏任务还是微任务


我们知道微任务是必须基于宏任务的,如果是单独按照挂起来说,那也是不正确的,因为宏任务也可能挂起!现在网上主流的理解是定时器属于宏任务,另外一种理解是属于微任务!说定时器是宏任务是因为按照定义,微任务必须基于宏任务,上述的Promise.then catch finally基于Promise,其余的微任务也一样。但是说定时器是微任务是因为定时器明显是延迟执行,符合微任务的特点。下面大家看段代码:

setTimeout(() => console.log(4))

new Promise(resolve => {
  console.log(1)
  resolve()
}).then(()=> {
  console.log(3)
})

console.log(2)

上述代码返回 1,2,3,4,原因很简单,我们现在分别用定时器是宏任务和微任务来解释。当定时器是宏任务时,浏览器事件执行下来,把定时器的回调推入宏任务栈,之后直接执行promise,因为不需要等待,输出1,之后因为Promise.then属于微任务,再次推入微任务的栈,之后继续执行主线程,输出2。之后我们知道微任务有优先执行权,所以执行promise.then,输出3,再执行挂起的定时器,输出4。 大家看到这里是不是有疑问,既然微任务是基于宏任务的,为啥new Promise这个宏任务执行完毕,不立刻执行依赖它的then这个微任务获得3,原因是我们把代码从头到位看成一个主线程,也就是必须要主线程跑完,才能执行微任务,这个时候微任务才有优先权。简单理解就是主线程是同步的,微任务是异步的,同步的跑完,才会走异步的。定时器的回调函数也是异步的,且定时器属于宏任务,所以在promise.then之后执行。 现在按照定时器是微任务的理论解释,其他的一样,定时器可以理解是属于主线程上的微任务,主线程执行完毕,主线程可以看成是父元素,里面的各个宏任务可以看成是子元素,子元素的微任务比父元素的微任务优先度高,所以最后才执行定时器。 大家觉得能接受哪种方式,就按照哪种方式理解,反正不影响结果。按照少数服从多数的原则,本文就按照定时器是宏任务来理解。

event Loop 的定义


前方高能,请注意: 事件循环过程简单介绍:首先有2个任务队列,宏任务队列和微任务队列,js会先从宏任务队列执行任务,如果宏任务队列为空,直接查看是否有对应的微任务,如果有微任务,循环执行微任务队列,然后判断是否渲染,如果没有微任务,直接判断是否渲染。如果宏任务队列不为空,先执行最前面的一个宏任务,然后按照上面的过程查看微任务队列是否为空,如果微任务存在,循环执行微任务,然后判断是否渲染,如果微任务队列为空,直接判断是否渲染。

如果小伙伴们觉得文字解说不清楚的,看下面的图片,因为网上的工具都要收费,所以小编只好截图截两次,大家合起来看就好

image

image

通过代码彻底理解event Loop


setTimeout(() => console.log(4))

new Promise(resolve => {
  console.log(1)
  resolve()
}).then(()=> {
  console.log(3)
})

console.log(2)

还是这段代码,参照上面的event Loop理论,首先解析整个html属于宏任务,所以打印出1,2,然后在解析html过程中,增加了微任务promise.then(),和宏任务定时器,当解析html这个宏任务执行完毕,我们开始循环执行已经挂起的微任务,所以打印出来了3,然后浏览器判断是否需要渲染,执行新一轮event Loop,也就是执行刚刚放入的定时器任务,于是出现了4。

后记


万事万物都有规则,掌握了规则,问题再难也能解决!

共收到 0 条回复