Skip to content

谈谈事件循环 / 轮询(Event-Loop)

什么是事件循环 / 轮循?

WHATWG规范中是这么定义事件循环的:

To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section.

为了协调事件,用户交互,脚本,渲染,联网等,用户代理必须使用本节中描述的事件循环。

从规定的定义中,我们可以了解事件用户交互脚本渲染网络请求这些东西,都会通过事件循环协调运作。

事件循环 / 轮循(Event Loop),其实就是JavaScript异步回调机制的实现原理

因为JavaScript是单线程运行的,异步要基于回调来实现

逐步讲解

示例代码

假设我们有这么一段JavaScript代码

js
console.log('开始')

setTimeout(() => {
    console.log('定时')
}, 1000)

console.log('结束')

可能大家都能快速得出答案

开始
结束
定时

那你知道它在JavaScript内部是如何执行的吗?

下面我们一步一步解析它的执行顺序

容器理解

先看下下面这个示例,它大概地抽象描述了JavaScript引擎的各个容器:

  • Call StackJavaScript的调用栈
  • Web APIsWeb应用接口,像setTimeoutDOM操作、Ajax请求、Promise请求之类的相关执行都放在里面
  • Event Loop:事件循环的执行状态
  • Callback Queue:回调队列
  • Browser Console:浏览器控制台

执行步骤

详细解析

下面详细解析一下步骤:

  1. script脚本或者在NodeJS的代码被执行时,事件循环就开启了
  2. 当线程运行时,遇到同步任务会被立即执行,微任务会加入微任务队列,宏任务会经过WebAPIs处理(处理定时或其他)后,放入宏任务队列
  3. 线程继续运行到下一个任务,同样,不同类型的任务处理方式如第2步一致,但是处理完这一次任务后,事件循环会先查看微任务队列有没有任务需要处理,如果有,优先处理微任务,在执行下一个任务
  4. 如此反复循环,直到所有同步任务和微任务执行完毕,开始按宏任务队列依次执行宏任务
  5. 宏任务执行完毕后,事件循环终止

事件循环与DOM渲染之间的关系

DOM渲染会尝试在每次事件循环执行完一个异步任务Call Stack为空的时候执行(并不一定会执行,或许会在多次轮循或者所有轮循执行完毕之后才执行)

顺序:

  1. 开始执行同步任务
  2. 同步任务执行完毕,CALL Stack 空闲
  3. 事件循环开始运行,执行一个异步任务
  4. 异步任务执行完毕,Call Stack为空
  5. 尝试渲染DOM(全部渲染完成则停止)
  6. 事件循环执行下一个异步任务
  7. 异步任务执行完毕,Call Stack为空
  8. 尝试渲染DOM(全部渲染完成则停止)
  9. ...
  10. 如此循环直至异步任务全部执行完毕,或者DOM全部渲染完毕