JavaScript异步机制(二)之任务队列和事件循环

在这个系列的第一篇调用栈以后,今天想说说任务队列。 首先简单说下浏览器的进程线程。以Chrome浏览器为例,Chrome浏览器使用多个进程来隔离不一样的网页。所以在Chrome中打开一个网页至关于起了一个进程。这一个进程中又包含多个线程,每一个线程分别处理不一样的任务,好比渲染页面,执行js等等。node

一个浏览器进程一般由如下五个线程组成:promise

  1. GUI渲染线程。
  2. JavaScript引擎线程。
  3. 事件触发线程。
  4. 定时器线程。
  5. 异步HTTP请求线程。

GUI 渲染线程

GUI渲染线程负责渲染浏览器界面HTML元素,当界面须要重绘(Repaint)或因为某种操做引起回流(reflow)时,该线程就会执行。在Javascript引擎运行脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被”冻结”了。浏览器

JavaScript引擎线程

Javascript引擎,也能够称为JS内核,主要负责处理Javascript脚本程序,例如V8引擎。Javascript引擎线程负责解析Javascript脚本,运行代码。数据结构

事件触发线程

当一个事件被触发时该线程会把事件添加到待处理事件队列的队尾,等待JS引擎的处理。这些事件能够是当前执行的代码块如定时任务、也可来自浏览器内核的其余线程如鼠标点击、AJAX异步请求等,但因为JS的单线程关系全部这些事件都得排队等待JS引擎处理。异步

定时器线程

浏览器定时计数器并非由JavaScript引擎计数的, 由于JavaScript引擎是单线程的,若是处于阻塞线程状态就会影响记计时的准确, 所以经过单独线程来计时并触发定时是更为合理的方案。函数

异步HTTP请求线程

在XMLHttpRequest在链接后经过浏览器新开一个线程请求,在检测到状态变动时,会触发相应的事件,若是设置有回调函数,异步线程就产生状态变动事件,将事件处理程序放到 JavaScript引擎的处理队列中等待处理。线程

那么什么是任务队列呢?server

任务队列

你们都知道JS是单线程的,假如在JS引擎执行代码的过程当中触发了一些事件,那引擎会停下手中的事情来处理这些事件吗?固然不可能。那何时才能执行这些事件处理程序呢?在js引擎空闲的时候。实际上,不管是事件触发仍是定时器仍是HTTP请求,在各自的线程处理好对应的API以后,会把各自的事件处理程序放到一个叫作任务队列的数据结构中。在JS引擎空闲的时候会按顺序去执行每个事件处理程序。按怎样的顺序?先进队列的先执行。这叫先进先出,和栈结构的后进先出正好相反。队列

事件循环

当JS引擎执行完代码以后,若是任务队列中有待处理的事件处理程序,那么JS引擎回去当即执行这些程序,那么假如如今任务队列是空的,可是过了一会有事件触发了,JS引擎是如何知道的呢?这就要讲到事件循环了。当JS引擎空闲的时候,他会不断的是轮询任务队列,若是有任务的话就去执行。进程

微任务

微任务包括promise的回调,mutationObserver的回调,以及nodejs的process.nextTick的回调。

宏任务

script所有代码。setTimeout,setInterval,setImmediate,I/O,UI Rendering

JS引擎初始执行代码若是遇到微任务,会将微任务放到微任务队列中,若是有宏任务,则将其放入宏任务队列。执行完代码后,会先去执行微任务队列中的事件,而后就是不断的事件循环。

咱们能够把初始代码当成一个宏任务,在执行完这个宏任务以后,JS引擎会去处理本轮宏任务结束时的微任务队列,而后再去处理宏任务队列。因此执行微任务的时机就是在宏任务与宏任务之间。

更新视图

浏览器更新视图的时机是由其本身决定的,并不必定是宏任务与宏任务之间必定有视图的更新。能够肯定的是,在JS引擎执行代码时,是不会更新视图的,由于GUI 渲染线程与JavaScript引擎线程互斥!浏览器会在适当的时机(宏任务与宏任务之间)更新视图,而后再继续执行代码。

GUI 渲染线程 与 JavaScript引擎线程互斥!

因为JavaScript是可操纵DOM的,若是在修改这些元素属性同时渲染界面(即JavaScript线程和UI线程同时运行),那么渲染线程先后得到的元素数据就可能不一致了。所以为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JavaScript引擎为互斥的关系,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到引擎线程空闲时当即被执行。

相关文章
相关标签/搜索