本章了解一下js的运行原理,了解了js的运行原理才能写出更优美的代码,提升运行效率,还能解决开发中遇到的不理解的问题。
进程是cpu资源分配的最小单位,进程能够包含多个线程。 浏览器就是多进程的,每打开的一个浏览器窗口就是一个进程。css
线程是cpu调度的最小单位,同一进程下的各个线程之间共享程序的内存空间。html
能够把进程看作一个仓库,线程是能够运输的货车,每一个仓库有属于本身的多辆货车为仓库服务(运货),每一个仓库能够同时由多辆车同时拉货,可是每辆车同一时间只能干一件事,就是运输本次的货物。这样就好理解了吧。面试
浏览器包括4个进程:浏览器
渲染进程主要包括GUI渲染线程、Js引擎线程、事件循环线程、定时器线程、http异步线程。性能优化
先看看浏览器获得一个网站资源后干了哪些事:网络
GUI就是来干这个事情的,若是修改了一些元素的颜色或者背景色,页面就会重绘(Repaint),若是修改元素的尺寸,页面就会回流(Reflow),当页面须要Repaing和Reflow时GUI多会执行,进行页面绘制。异步
这里提示一点:Reflow比Repaint的成本更高,在js性能优化中会将如何避免Reflow和Repaint函数
js引擎线程就是js内核,负责解析与执行js代码,也称为主线程。浏览器同时只能有一个JS引擎线程在运行JS程序,因此js是单线程运行的。oop
须要注意的是,js引擎线程和GUI渲染线程同时只能有一个工做,js引擎线程会阻塞GUI渲染线程post
<html> <body> <div id="div1"> a </div> <script> document.getElementById('div1').innerHTML = 'b' </script> <div id='div2'> div2 </div> </body> </html>
在浏览器渲染的时候遇到<script>标签,就会中止GUI的渲染,而后js引擎线程开始工做,执行里面的js代码,等js执行完毕,js引擎线程中止工做,GUI继续渲染下面的内容。因此若是js执行时间太长就会形成页面卡顿的状况,这也是后面性能优化的点。
事件循环线程用来管理控制事件循环,而且管理着一个事件队列(task queue),当js执行碰到事件绑定和一些异步操做时,会把对应的事件添加到对应的线程中(好比定时器操做,便把定时器事件添加到定时器线程),等异步事件有告终果,便把他们的回调操做添加到事件队列,等待js引擎线程空闲时来处理。
因为js是单线程运行,因此不能抽出时间来计时,只能另开辟一个线程来处理定时器任务,等计时完成,把定时器要执行的操做添加到事件任务队列尾,等待js引擎线程来处理。这个线程就是定时器线程。
当执行到一个http异步请求时,便把异步请求事件添加到异步请求线程,等收到响应(准确来讲应该是http状态变化),把回调函数添加到事件队列,等待js引擎线程来执行。
上面介绍了渲染进程中的5个主要的线程,可能看完上面对各个线程简单的介绍,还有点不明白他们之间到底怎么协做工做的,下面就从Event Loop的角度来聊一聊他们之间是怎样那么愉快合做的。
已经知道了js是单线程运行的,也知道js中有同步操做和异步操做。同步和异步你们应该很熟了,很少介绍。
同步操做运行在js引擎线程(主线程)上,会造成一个执行栈,而异步操做则在他们对应的异步线程上处理(好比:定时操做在定时器线程上;http请求则在异步请求线程上处理)。
而事件循环线程则监视着这些异步线程们,等异步线程们里面的操做有告终果(好比:定时器计时完成,或者http请求获取到响应),便把他们的毁掉函数添加到事件队列尾部,整个过程当中执行栈、事件队列就构成Event Loop。
请看网络盗图:
这是网络上对Event Loop的解释图,相信你们如今能明白这张图的含义了。
定时器是规定在一段时间以后执行一段代码,可是在js执行中不会准确无误的按照预期的时间去执行定时器里面的代码。
一个缘由是W3C标准规定setTimeout中最小的时间周期是4毫秒,凡是低于4ms的时间间隔都按照4ms来处理。
其实还有一个重要的缘由,若是仔细看上面的文章,你们应该会想到在js执行的时候,主线程碰到定时器的时候,是不会直接处理的,应该是先把定时器事件交给定时器线程去处理,这时主线程继续执行下面的代码,同时定时器线程开始计时处理,等到计时完毕,事件循环线程会把定时器要执行的操做放在事件队列末尾,等主线程空闲的时候再来执行事件队列里面的操做。
使用setTimeout模拟setInterval代码相似如下代码:
var say = function() { setTimeout(say, 1000) console.log('hello world') } setTimeout(say, 1000)
这样js碰到定时器,会交给定时器线程处理,而后等计时完毕,定时器里面的操做添加到事件队列,等主线程空闲去执行,主线程执行的时候又会发遇到定时器,这是又开始执行上面的一系列操做。
你会发现,这样作会在每一次定时器执行完毕才开始下一个定时器,其中的偏差只是等待主线程空闲所须要等待的时间。
而setInterval是规定每隔固定的时间就往定时器线程中推入一个事件,这样作有一个问题,就是累积效应。
累积效应会致使有些事件丢失,具体为何会丢失,感兴趣的能够看这篇文章,因此为了保险起见,尽可能去使用setTimeout而不使用setInterval。
若是有对setTimeout很是感兴趣的同窗,我很是推荐你们去看看80% 应聘者都不及格的 JS 面试题这篇文章。
microtask是Promise里一个新的概念。
请看网络盗图:
因此js运行过程:
有关macrotask和microtask的分析借鉴于从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理