美文网首页javascript我爱编程
浏览器的运行机制—3.浏览器的渲染进程

浏览器的运行机制—3.浏览器的渲染进程

作者: YINdevelop | 来源:发表于2018-04-12 13:42 被阅读245次

    重点来了,我们可以看到,上面提到了这么多的进程,那么,对于普通的前端操作来说,最终要的是什么呢?答案是渲染进程。因为页面的渲染,JS的执行,事件的触发,都在这个进程内进行的。接下来重点分析这个进程。

    之前讲到过,进程一般是多线程的,忘了可以再复习下前面第一节,进程和线程,那么浏览器的渲染进程又包括哪些线程。

    1.渲染进程包括哪些线程

    1. GUI渲染线程
    • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
    • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
    1. JS引擎线程(单线程)
    • 也称为JS内核,负责处理Javascript脚本程序。(例如常常听到的谷歌浏览器的V8引擎,新版火狐的JaegerMonkey引擎等)
    • JS引擎线程负责解析Javascript脚本,运行代码。
    • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
    • 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
    1. 事件触发线程
    • 归属于渲染进程而不是JS引擎,用来控制事件轮询(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
    • 当JS引擎执行代码块如鼠标点击、AJAX异步请求等,会将对应任务添加到事件触发线程中
    • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理任务队列的队尾,等待JS引擎的处理
    • 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
    1. 定时触发器线程

      • 定时器setInterval与setTimeout所在线程
      • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果任务队列处于阻塞线程状态就会影响记计时的准确)
      • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
      • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
    2. 异步http请求线程

      • 用于处理请求XMLHttpRequest,在连接后是通过浏览器新开一个线程请求。如ajax,是浏览器新开一个http线程
      • 将检测到状态变更(如ajax返回结果)时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入js引擎线程的事件队列中。再由JavaScript引擎执行。


        2084336019-5a65972413011.png

    知道了这几个线程,那么通过这几个线程,js是怎么执行的呢?

    2.渲染进程中的线程之间的关系

    GUI渲染线程与JS引擎线程互斥

    由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和GUI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。

    因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起,
    GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。

    JS阻塞页面加载

    从上述的互斥关系,可以推导出,JS如果执行时间过长就会阻塞页面。

    譬如,假设JS引擎正在进行巨量的计算,所以JS引擎很可能很久很久后才能空闲,所以导致页面渲染加载阻塞。这就牵扯到script标签在html中的存放位置。具体可以看我另一篇文章 为什么script标签一般放在body下面

    3.js引擎是单线程的

    我们知道js是单线程的。也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。

    参考阮一峰大神的文章js事件轮询(Event Loop)

    • JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
    • 所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
    • 为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

    4.js事件轮询

    上面我们已经知道JS引擎是单线程,任务应该是按顺序执行的,那么怎么会有同步异步之说?

    • 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
    • 如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
    • JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
    • 于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

    理解了同步异步。其实其最本质原因就是基于js的事件轮询机制。

    1. 所有同步任务都在主线程(即js引擎线程)上执行,形成一个执行栈
    2. 而异步任务均由事件触发线程控制,其有一个任务队列。只要异步任务有了运行结果,就在"任务队列"之中放置回调事件。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。所以所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。
    3. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",按顺序结束等待状态,进入执行栈,开始执行。
    4. 主线程不断重复上面的第三步
    5. 只要主线程空了,就会去读取"任务队列",这个过程会不断重复。这就是JavaScript的运行机制。又称为Event Loop(事件循环或者轮询)。

    5.定时器触发线程

    上述事件循环机制的核心是:JS引擎线程和事件触发线程

    js来控制主线程,事件触发来控制任务队列就如主线程。

    为什么要单独的定时器线程?因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确,因此很有必要单独开一个线程用来计时。

    什么时候会用到定时器线程?当使用setTimeout或setInterval时,它需要定时器线程计时,计时完成后就会将特定的事件推入事件触发线程的任务队列中。等待进入主线程执行。

    譬如:

    setTimeout(function(){
        console.log('hello!');
    }, 1000);
    

    这段代码的作用是当1000毫秒计时完毕后(由定时器线程计时),将回调函数推入事件队列中,等待主线程执行

    setTimeout(function(){
        console.log('hello!');
    }, 0);
    
    console.log('begin');
    
    //begin hello
    

    这段代码的效果是表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。

    注意:

    • 虽然代码的本意是0毫秒后就推入事件队列,但是html5标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
    • 就算不等待4ms,就算假设0毫秒就推入事件队列,也会先执行begin(因为只有主线程可执行栈内空了后才会主动读取事件队列)。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。同理setInterval则是每次都精确的隔一段时间推入一个事件(但是,事件的实际执行时间不一定就准确,还有可能是这个事件还没执行完毕,下一个事件就来了)

    6.总结

    这里我没有总结css渲染这块,由于内容较多,我会另开一篇文章来讲解。

    写了这么多,还是要感谢大神们的文章。

    js事件轮询(Event Loop)

    从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理

    相关文章

      网友评论

        本文标题:浏览器的运行机制—3.浏览器的渲染进程

        本文链接:https://www.haomeiwen.com/subject/kxbxkftx.html