Event Loop

114 阅读2分钟

进程与线程

进程:一个程序的运行时,是系统分配和调度资源的基本单元。

线程:是进程中相对独立的,可调度的执行单元。一个进程含有一至多个线程,一个线程一定属于一个进程。

JS为什么是单线程的?

为了避免DOM冲突,因为JS浏览器脚本,主要是与用户互动,并且能够操作DOM。假如JS是多线程的,如果对DOM某个节点上同时进行增删操作的话,那么会引起渲染引擎的混乱。因此JS是单线程,以后也将一直是单线程的。

执行栈

一个存储函数调用的栈结构,遵循后进先出的原则,当任务执行完之后,最后执行完的任务最先弹出栈。

任务队列 遵循先进先出的原则。

任务分为两种:同步任务和异步任务;

  • 同步任务都在主线程上执行,形成执行栈;
  • 主线程之外有一个任务队列,是异步任务返回结果按照先后顺序放入任务队列中等待执行;
  • 一旦执行栈中的任务执行完毕,系统将读取任务队列,任务队列中的任务会按照先进先出的顺序进入执行栈中执行;
  • 主线程不断重复第三步。

所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

Event Loop中任务分为微任务和宏任务:

宏任务有: script、setTimeout、setInterval ,setImmediate ,I/O ,UI rendering;

微任务有:process.nextTick ,promise ,MutationObserver,其中 process.nextTick 为 Node 独有。

执行顺序为:

  • 先执行宏任务中的script,也就是同步任务;
  • 遇到setTimeout、setInterval ,setImmediate时,会将setTimeout、setInterval,setImmediate推入相应任务队列中等待执行;
  • 执行完同步任务之后执行微任务,首先执行微任务中的process.nextTick,然后是promise中then的回调函数;
  • 执行完所有的微任务之后,然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是setTimeout和setInterval和setImmediate中的回调函数。

NodeJS中的Event Loop与浏览器中的Event Loop是两种不同的运行机制。

NodeJS中的Event Loop运行机制是:

  • V8引擎解析JavaScript脚本。
  • 解析后的代码,调用Node API。
  • libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
  • V8引擎再将结果返回给用户。