JavaScript之多线程和Event-Loop
引子几乎在每一本JS相关的书籍中,都会说JS是单线程的,JS是通过事件队列(Event Loop)的方式来实现异步回调的。 对很多初学JS的人来说,根本搞不清楚单线程的JS为什么拥有异步的能力,所以,我试图从进程、线程的角度来解释这个问题。 CPU说到CPU和进程、线程,对计算机操作系统有过学习和了解的同学应该比较熟悉。 计算机的核心是CPU,它承担了所有的计算任务。 它就像一座工厂,时刻在运行。 假定工厂的电力有限,一次只能供给一个车间使用。 也就是说,一个车间开工的时候,其他车间都必须停工。 背后的含义就是,单个CPU一次只能运行一个任务。 进程就好比工厂的车间,它代表CPU所能处理的单个任务。 进程之间相互独立,任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。 CPU使用时间片轮转进度算法来实现同时运行多个进程。 CPU、进程、线程之间的关系从上文我们已经简单了解了CPU、进程、线程,简单汇总一下。 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)不同进程之间也可以通信,不过代价较大单线程与多线程,都是指在一个进程内的单和多浏览器是多进程的我们已经知道了CPU、进程、线程之间的关系,对于计算机来说,每一个应用程序都是一个进程, 而每一个应用程序都会分别有很多的功能模块,这些功能模块实际上是通过子进程来实现的。 对于这种子进程的扩展方式,我们可以称这个应用程序是多进程的。 而对于浏览器来说,浏览器就是多进程的,我在Chrome浏览器中打开了多个tab,然后打开windows控制管理器: 如上图,我们可以看到一个Chrome浏览器启动了好多个进程。总结一下: 浏览器是多进程的每一个Tab页,就是一个独立的进程浏览器包含了哪些进程主进程 协调控制其他子进程(创建、销毁)浏览器界面显示,用户交互,前进、后退、收藏将渲染进程得到的内存中的Bitmap,绘制到用户界面上处理不可见操作,网络请求,文件访问等第三方插件进程 每种类型的插件对应一个进程,仅当使用该插件时才创建GPU进程 用于3D绘制等渲染进程,就是我们说的浏览器内核 负责页面渲染,脚本执行,事件处理等每个tab页一个渲染进程那么浏览器中包含了这么多的进程,那么对于普通的前端操作来说,最重要的是什么呢? 答案是渲染进程,也就是我们常说的浏览器内核 浏览器内核(渲染进程)从前文我们得知,进程和线程是一对多的关系,也就是说一个进程包含了多条线程。 而对于渲染进程来说,它当然也是多线程的了,接下来我们来看一下渲染进程包含哪些线程。 GUI渲染线程 负责渲染页面,布局和绘制页面需要重绘和回流时,该线程就会执行与js引擎线程互斥,防止渲染结果不可预期JS引擎线程 负责处理解析和执行javascript脚本程序只有一个JS引擎线程(单线程)与GUI渲染线程互斥,防止渲染结果不可预期事件触发线程 用来控制事件循环(鼠标点击、setTimeout、ajax等)当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中定时触发器线程 setInterval与setTimeout所在的线程定时任务并不是由JS引擎计时的,是由定时触发线程来计时的计时完毕后,通知事件触发线程异步http请求线程 浏览器有一个单独的线程用于处理AJAX请求当请求完成时,若有回调函数,通知事件触发线程当我们了解了渲染进程包含的这些线程后,我们思考两个问题: 为什么 javascript 是单线程的为什么 GUI 渲染线程为什么与 JS 引擎线程互斥为什么 javascript 是单线程的首先是历史原因,在创建 javascript 这门语言时,多进程多线程的架构并不流行,硬件支持并不好。 其次是因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。 而且,如果同时操作 DOM ,在多线程不加锁的情况下,最终会导致 DOM 渲染的结果不可预期。 为什么 GUI 渲染线程与 JS 引擎线程互斥这是由于 JS 是可以操作 DOM 的,如果同时修改元素属性并同时渲染界面(即 JS线程和UI线程同时运行), 那么渲染线程前后获得的元素就可能不一致了。 因此,为了防止渲染出现不可预期的结果,浏览器设定 GUI渲染线程和JS引擎线程为互斥关系, 当JS引擎线程执行时GUI渲染线程会被挂起,GUI更新则会被保存在一个队列中等待JS引擎线程空闲时立即被执行。 ...