js循环机制EventLoop

84 阅读3分钟

为什么会有事件循环和任务队列

JS是单线程,即同一时间只能做一件事情;为什么是单线程呢?

JavaScript的单线程,与它的用途有关。

作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。

这决定了它只能是单线程,否则会带来很复杂的同步问题。为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JS脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JS单线程的本质。

比如,假定JavaScript同时有两个线程,

一个线程在某个DOM节点上添加内容,

另一个线程删除了这个节点,

这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

任务队列

什么是任务队列?

既然JS是单线程,那就意味着所有任务只能排队一个一个的执行

万一某个任务耗时很长,那后面的只能一直等前面执行完才能继续执行。CPU忙不过来,倒也算了,但是很多时候CPU是闲着的

JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

任务分类

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

异步任务又可以分为两种:

宏任务:script, setTimeout, setInterval, setImmeditate, T/O, UI rendering

微任务:process, nextTick, promise.then(), object.observe, MutationObserver

运行机制

  1. 所有的同步任务都在主线程的执行栈中就行执行
  2. 除了主线程之外还有一个任务队列 ,只有异步返回运行结果,就在任务队列放置一个事件
  3. 执行栈中的同步任务执行完后,将任务队列中返回结果的异步任务放置执行栈中执行

执行顺序

  1. 先执行主线程

  2. 遇到宏队列(macrotask)放到宏队列(macrotask)

  3. 遇到微队列(microtask)放到微队列(microtask)

  4. 主线程执行完毕

  5. 执行微队列(microtask) 判断微任务队列是否存在异步任务,若存在的话执行2和3 微队列(microtask)执行完毕

  6. 执行一次宏队列(macrotask)中的一个任务 判断宏任务队列是否存在异步任务,若存在的话执行2和3 宏队列(microtask)执行完毕

  7. 执行微队列(microtask),执行完毕

  8. 依次循环。。。

流程图.jpg