JS事件循环机制(Event-Loop)的原理

810 阅读3分钟

本次我们所探讨内容的涉及的知识点

  • JavaScript是单线程的
  • EventLoop是JavaScript的一种执行机制
  • 任务队列与主线程
  • 什么是CallStack
  • 宏微任务的分类
  • 最新的尾递归优化

bg2014100802.png

目前主流针对Event-Loop的解释

在讲EventLoop之前,咱们需要先了解一下,主线程和任务队列。在JavaScript中,所有的任务都可以分为两种,一种是同步任务,一种是异步任务。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
而主线程从"任务队列"中读取事件,这种持续不断执行的运行机制又称为Event Loop(事件循环)。

执行栈和执行上下文的关系

众所周知,在JavaScript中有两个核心概念,原型链(prototype chain)和执行上下文(execution context)。而咱们在浏览器中的CallStack和经常提到的执行栈就是与执行上下文相关。
当浏览器第一次加载脚本时,默认情况下,他会进入全局上下文。如果在全局代码中调用了一个函数,则代码的执行会进入函数中,此时会创建一个新的执行上下文,它会被推到执行上下文栈中,也称为压栈。而提到的执行上下文栈则为执行栈(CallStack)。

执行上下文针对尾递归的优化

在之前正常的递归都会执行调用一次function都要声明一个新的执行上下文,并向执行上下文栈压栈。这大大的加大了内存的开销,后js引擎针对尾递归做了优化,如果是在函数体的最后return的是方法本身,那么并不会声明一个新的执行上下文,而是复用或者说restart原本的执行上下文,传入对应的参数。

EventLoop的原理

像刚刚提到的是"任务队列"通知主线程,某个异步任务可以执行了,然后该任务才会进入主线程执行的呢,这个机制是如何实现的呢?以下分为宏任务和微任务做讲解
微任务真实的执行机制如下:

  1. 将正在执行的上下文复制为一个异步上下文(asyncContext)
  2. 执行异步函数的函数体
  3. 当函数体执行完成,从上下文栈中移除当前的上下文,并向上下文栈压入一个重新恢复该异步上下文,执行异步函数的断言(resolve or reject

可能大家会有疑问执行上下文栈如何判断是执行上下文还是异步上下文,这个是通过标记法实现的,异步上下文在执行时声明的是AsyncEnvironmentRecord

宏任务的执行机制则是存在排队,等时间到了之后向执行上下文压栈

这也从另一方面解释了,为什么如果有微任务会先执行微任务,但是如果setTimeOut时间的时间到了会先执行,setTimeOut的时间

END

以上代表个人观点,希望各位大佬指正,如果有更好的观点可以留言,我会认真对待!