Event Loop 详解

182 阅读4分钟

本文详细介绍Event Loop 相关的知识点,以及在实际场景中的应用示例

相关知识:

  • js 是一门单线程的语言,即同一时间只能执行一个任务;
  • 回调函数

简单理解,回调函数就是一个函数,将这个函数作为一个参数传到另一个函数里面,当另一个函数执行到拥有一定结果的时候,通过执行传递进来的参数函数,将这个结果带回。这个过程就叫做回调,一般回调函数都是用来解决异步问题的。

  • 队列 (queue)

队列是一种FIFO(First In First Out)的数据结构,特点就是先进先出

  • 栈 (stack)

栈是一种LIFO(Last In first Out) 的数据结构,特点就是后进先出

  • 事件表格

Event Table 可以理解为一种 事件 --- 回调函数的对应表

  • 事件队列

就是回调函数的队列,所以也叫callback queue;

当event table中的事件被触发,事件对应的回调函数就会被push进这个event queue,然后等待被主线程调用执行;

js引擎首先判断代码是同步还是异步,同步就进入主线程,
异步事件会先进入Event Table并注册为函数
当异步任务指定的事情完成以后,才会将函数移入事件队列中
所以其实事件队列中就是等待被执行的回调函数 (所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。)

EventLoop_1.png

  • 执行栈(也叫调用栈)Call Stack

当我们调用一个方法时,js会生成一个与这个方法相对应的执行环境,也叫做执行上下文。这个执行环境中存在着这个方法的私有作用域,参数,this对象等等。因为js 是单线程的,同一时间只能执行一个方法,所以当一系列的方法被调用时,js会先解析这些方法,把其中的同步任务按执行顺序排列到一个地方,这个地方叫做执行栈。Event loop会一直检查call stack 中是否有函数需要执行,如果有,就从栈顶依次执行。同时,如果在过程中发现其他函数,继续入栈然后执行。

JS的运行机制

  1. 所有同步任务都在主线程上执行,形成一个执行栈;
  2. 除了主线程外,还存在一个或多个‘任务队列’,用来存放异步函数;
  3. 一旦‘执行栈’中的所有同步任务执行完毕,系统都会读取 ‘任务队列’ 中有没有任务,如果有,就读取执行,一直循环 读取-执行 操作

JS中有的两种异步任务

  • 宏任务
  • 微任务

宏任务是由宿主发起的,而微任务由JavaScript自身发起。

EventLoop_2.png

注意: 微任务永远在宏任务之前执行

Event Loop具体执行步骤

注意:Promise中,then中的才是异步的微任务,主体方法是同步任务

EventLoop_3.png

Event Loop的事件执行顺序图:

EventLoop_4.jpeg

上图可知:

一轮事件轮询中,微任务永远在宏任务之前执行(如果有微任务的话)

  • 总结:

    先处理主线程上的同步任务,在执行异步任务,而异步任务分为 宏任务和微任务,其中微任务永远在宏任务之前执行。

    同步任务--(微任务--宏任务)-->下一轮...

具体示例

示例一

<script>
    console.log(1)
    setTimeout(() => console.log(5),0)
    new Promise((resolve, reject) => {
        console.log(2)
        resolve()
    }).then(() => {
        console.log(4)
    })
    console.log(3)
</script>

详解:

  1. 整个script 就是一个宏任务,主线程开始执行任务
  2. 先执行同步任务console.log(1),然后遇到setTimeout发现是一个异步的宏任务, 执行setTimeout 并把注册的毁掉函数函数分发到宏任务的事件队列中等待执行。
  3. 然后遇到new Promise ,立即执行 promise 函数 console.log(2),并将异步微任务promise.then注册分发到微任务的事件队列中等待执行,执行同步任务console.log(3)
  4. 第一轮事件轮询结束,主线程开始检查异步任务,优先检查微任务事件队列, 发现promise.then, 执行
  5. 微任务执行结束后,开始检查宏任务的事件队列,发现setTimeout 执行

示例二 (复杂)

loop_1.png

解析如下:

loop_2.png

loop_3.png