Event Loop

Event Loop

JavaScript是一门单线程语言,同一时间只能做一件事情。在js中有两类任务:

  • 同步任务
  • 异步任务

在js主线程中的任务执行:

1、同步和异步任务分别进入不同的“场所”执行。所有同步任务都在主线程上执行,形成一个执行栈,而异步任务进入Rvent Table并注册回调函数。

2、当这个异步任务有了运行结果,Event Table会将这个回调函数移入Event Queue,进入等待状态。

3、当主线程同步任务执行完成,会失去Event Queue读取对应的函数,并结束它的等待状态,进入主线程执行。

4、主线程不断重复上面3个步骤,也就是常说的Event Loop(事件循环)

宏任务和微任务

除了广义的同步任务和异步任务,任务还有更精细的定义:

  • 宏任务(macro-task):包括整体代码script、setTimeOut、setInterval、I/O、UI交互事件,可以理解是每次执行栈执行的代码就是一个宏任务
  • 微任务(micro-task):Promise,process.nextTick,且process.nextTick的优先级大于Promise.then,可以理解是在当前task执行结束后立即执行的任务

事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环,接着执行所有的微任务,然后再从宏任务开始,找到其中一个任务队列执行完毕,在执行所有的微任务。

setTimeOut(fn, 0)在下一轮事件循环开始时执行,Promise.then在本轮事件循环结束时执行。

不同类型的任务会进入对应的Event Queue:

Promise中的异步体现在thencatch中,所以写在Promise中的代码是被当做同步任务执行的。

await实际上是让出线程的标志。await后面的表达式会先执行一遍,将await后面的代码加入到microtask中,然后就会跳出整个async函数来执行后面的代码。

由于async/await本身是promise+generator的语法糖,所以await后面的代码是microtask。

Node中的Event Loop

Node中Event Loop和浏览器中的完全不同。

Node的Event Loop分为六个阶段,它们会按照顺序反复执行。每当进入一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。

  • timer:这个阶段会执行setTimeoutsetInterval回调,并且是由poll阶段控制的。
  • I/O:这个阶段会处理一些上一轮循环中少数未执行的I/O回调。
  • idle,prepare
  • poll:这是一个至关重要的阶段。这一阶段系统会做两件事:
    • 回到timer阶段执行回调。
    • 执行I/O回调
  • check:这个阶段执行setImmdiate
  • close callback:执行close事件

Event Loop