前端-JavaScript事件循环机制(Event Loop)

173 阅读3分钟

一、JavaScript为什么是单线程语言

1、浏览器环境的需求:JavaScript最初是为浏览器设计的,用于处理用户交互和动态内容。单线程模型避免了多个线程同时操作DOM的问题,从而减少了复杂性和潜在的错误。

2、避免竞态条件和死锁:在多线程环境中,多个线程可能会同时访问和修改相同的资源,导致竞态条件和死锁等问题。单线程模型通过强制顺序执行代码,避免了这些复杂性。

3、事件驱动的非阻塞模型:JavaScript采用事件驱动的非阻塞模型,通过事件循环来处理异步操作。这种模型允许JavaScript在单线程中高效地执行I/O操作,如网络请求、定时器和用户输入等,而不会阻塞主线程。

二、进程和线程

  • 进程:进程是操作系统分配资源(如内存、文件句柄等)的基本单位。每个运行的程序至少有一个进程。
  • 线程:线程是进程内的一个执行单元,也是CPU调度的基本单位。一个进程可以包含多个线程,这些线程共享进程的资源。

三、宏任务和微任务

概念:在JavaScript中,宏任务(Macro Task)和微任务(Micro Task)是异步任务执行机制的一部分,用于管理任务的调度和执行顺序

为什么区分宏任务和微任务:为了优化JavaScript的异步任务调度,使得事件循环能够高效地管理和执行任务,从而提高应用程序的性能和响应性。

  • 宏任务

    • <script>标签:当浏览器解析HTML文档时,遇到一个<script>标签(无论是内联的还是外部引入的),会立即停止解析文档,执行这个<script>标签内的代码,整个过程是一个宏任务,而在<script>标签内,可以创建微任务。

    • setTimeout:延时定时器任务。

    • setInterval:循环定时器任务。

    • setImmediateNode.js特有):当前事件循环结束后、下一个事件循环开始之前执行。

    • I/O操作:文件读取、网络请求等。

    • UI渲染:浏览器渲染引擎的渲染操作。

    • 事件处理:用户交互事件(如点击、键盘事件)。

  • 微任务

    • Promise:Promise的thencatchfinally回调。

    • MutationObserver:监控DOM变动的回调。

    • process.nextTickNode.js特有):同步任务执行之后,微任务执行之前执行。

    • queueMicrotask:专门用于调度微任务的方法。

四、一个事件循环执行顺序

graph LR
同步任务 --> process.nextTick --> 微任务 --> DOM渲染 --> 宏任务 --> setImmediate

五、事件循环机制

1、同步代码交给js主线程,异步代码交给宿主环境。

2、同步代码放入执行栈,异步代码等待时机成熟后由宿主环境将回调推送给任务队列中排队。

3、执行栈中同步任务执行完毕,会去任务队列看是否有异步任务,异步任务分为微任务和宏任务。

4、先将所有微任务全部放入执行栈中全部执行,再取第一个宏任务放入执行栈执行。

5、重复上述过程,就构成了事件循环(Event Loop)

PS:如果宏任务执行过程中产生了微任务,这些微任务会在当前宏任务执行完毕后立即执行,而不需要等待下一次事件循环。

js事件循环机制.png

六、事件循环何时结束

  • 没有任何待处理的任务,包括同步任务、宏任务和微任务。
  • 程序退出或环境销毁
  • 在Node.js中,可以使用process.exit()显式退出事件循环和整个进程