JS事件循环机制(Event Loop)

91 阅读3分钟

前言

说到js的事件循环机制,还得从js单线程开始说起,由于单线程只有一个执行流,必须等当前任务完成后才能继续执行下一个任务。如果一个任务运行时间过长则会阻塞UI渲染和用户交互,导致页面卡顿,无响应。另外也无法充分利用多核CPU的性能,限制了程序的并发处理能力。 为了解决这个弊端,js采用了以下2种方式:

  1. 异步编程:通过回调函数、Promise、async/await等机制来处理耗时的操作,不会阻塞主线程的执行,使得在耗时任务进行的同时,主线程可以继续处理其他任务。

  2. Web Workers:创建新的线程来执行复杂的计算任务,避免阻塞主线程,开启Web Workers只能在浏览器环境中,是专门为浏览器设计的用于在后台执行脚本的技术,以分担一些复杂或耗时的计算任务。Web Workers与主线程之间的通信是通过消息机制进行的,它们不能直接共享内存和变量,也不能直接操作DOM等主线程中的对象。所以,从整体上看,js仍然保持着单线程执行脚本和操作DOM的特性。 从第一点我们可以看出,异步基于回调来实现的,而Event Loop是异步回调的实现原理。

Event Loop的执行过程

console.log(1)
setTimeout(function demo(){
  console.log(2)
}, 3000)
console.log(3)

上面这段代码的执行结果非常简单:1 3 2,下面就以这段简单的代码来演示Event Loop不太简单的过程。在演示之前,我们要弄清楚js代码是如何执行的,主要有3点:

  • 从上往下,一行一行执行
  • 如果某一行代码报错,则停止后续代码的执行
  • 先把同步代码执行完,再执行异步代码

上面这几行代码的执行涉及到几个模块
(1)Browser console(浏览器控制台,这个大家都知道)
(2)Call Stack(调用栈,代码都是在这执行的)
(3)Web APIs(浏览器定义的API,比如setTimeoout)
(4)Callback Queue(回调队列,回调或异步任务都在这排着队)
(5)Event Loop(事件循环,一直在转)

执行过程:
(1)console.log(1)被推入Call Stack执行,Browser console会打印1,执行完毕,Call Stack清空
(2)执行setTimeout函数时,会在Web APIs中开启一个定时器,将demo函数放到定时器中,3s后推入Callback Queue执行
(3)setTimeout在Call Stack中被清空,开始执行console.log(3),Browser console打印3
(4)当同步代码都执行完了,Event Loop开始发挥作用,轮询查找Callback Queue要执行的函数
(5)3s到了,demo函数被推到Callback Queue,Event Loop发现了,将demo函数推到Call Stack中执行,Browser console打印2,执行完毕,清空Call Stask,到此执行完毕。

另外,DOM事件也是使用回调来执行,基于Event Loop。