JS学习笔记——由setTimeout面试题引出的浏览器(内核)渲染进程话题

1、setTimeout面试题

这几天看到一个有趣的问题,是关于setTimeout的,题目如下:

问:是页面是先渲染出1000个元素,还是控制台先打印出数据?如果渲染1000000个元素,谁先进行?

<div id="app"></div>
<script>
    window.onload = function () {
     
        let startTime = +new Date();
        let oApp = document.getElementById('app');

        for(let i=0; i<1000; i++) {
     
            let oP = document.createElement('p');
            oP.innerHTML = i;
            oApp.appendChild(oP);
        }
        
        let endTime = +new Date();
        console.log('输出时间', startTime , endTime, endTime - startTime);
    };
</script>

我凭直觉回答先在控制台打印,但是不知道正确不,于是打开浏览器,试了n多次

JS学习笔记——由setTimeout面试题引出的浏览器(内核)渲染进程话题_第1张图片

发现很难区别哪个更快,渲染DOM和打印几乎同时进行,于是我又把渲染DOM增加到100000个,再次尝试

<div id="app"></div>
<script>
    window.onload = function () {
     
        let startTime = +new Date();
        let oApp = document.getElementById('app');

        for(let i=0; i<100000; i++) {
     
            let oP = document.createElement('p');
            oP.innerHTML = i;
            oApp.appendChild(oP);
        }
        
        let endTime = +new Date();
        console.log('输出时间', startTime , endTime, endTime - startTime);
    };
</script>

这次效果就比较明显,控制台先打印出秒数,然后页面上DOM进行渲染
JS学习笔记——由setTimeout面试题引出的浏览器(内核)渲染进程话题_第2张图片
但是这种好像跟我们预期的效果不一样,正常开发来说,顺序应该是:在页面上渲染出数据然后再在控制台中进行其他操作

这时候我们该怎么办呢?没错,setTimeout就可以帮助我们解决。

console.logsetTimeout包裹,然后我们看一下效果

<div id="app"></div>
<script>
    window.onload = function () {
     
        let startTime = +new Date();
        let oApp = document.getElementById('app');

        for(let i=0; i<100000; i++) {
     
            let oP = document.createElement('p');
            oP.innerHTML = i;
            oApp.appendChild(oP);
        }
        
        let endTime = +new Date();
		setTimeout(function(){
     
        	console.log('输出时间', startTime , endTime, endTime - startTime);
        })
    };
</script>

JS学习笔记——由setTimeout面试题引出的浏览器(内核)渲染进程话题_第3张图片
很明显,在添加完setTimeout之后,页面上先渲染DOM元素,然后才在控制台打印出数据

2、浏览器(内核)渲染进程

2.1、进程和线程

在聊之前,先补充一下进程和线程的概念(面试常考

  • 进程:cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
  • 线程:cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)

注意浏览器是多进程的

2.2、浏览器的进程和线程

JS学习笔记——由setTimeout面试题引出的浏览器(内核)渲染进程话题_第4张图片
其中:

  • Browser进程:浏览器的主进程,只有一个
  • 第三方插件进程:每种类型的插件对应一个进程,仅当使用时创建
  • GPU进程:用于3D绘制等,最多一个
  • 浏览器渲染进程:页面渲染、脚本执行、事件处理等

而在所有进程中,我们关心浏览器渲染的进程,这里面包含了以下多个线程:

1、GUI渲染线程:

  • 负责渲染浏览器界面,解析HTML、CSS,构建DOM树和CSSOM数,构建RenderObject树,布局Layout和绘制Painting等
  • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行

2、JS引擎线程:(也称为JS内核,负责处理Javascript脚本程序。例如V8引擎)

  • JS引擎线程负责解析Javascript脚本,运行代码
  • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序

注意:

GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞

3、事件触发线程:

  • 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
  • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击AJAX异步请求等),会将对应任务添加到事件线程中
  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理

注意:

由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

4、定时触发器线程:

  • 传说中的setIntervalsetTimeout所在线程
  • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
  • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)

注意:

W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。比如我们给setTimeout的第二个参数设置为0时,会默认为最低标准4ms

5、异步HTTP请求线程:

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

而对于setTimeoutsetInterval,我们要清楚:

  • setTimeout:间隔指定时间后执行一次回调
  • setInterval:间隔指定时间循环执行回调

注意:

1、setTimeout(fn, n)这里的间隔指定时间n后执行一次回调是指,fn将会在n秒后被推入任务队列

2、setInterval(fn, n)不能保证两个函数之间调用的间隔一定是指定的时间n,如果两个周期的间隔为2n,但是函数fn执行时间大于n,这就会使时间间隔小于n,依次往后,会影响整体流程

3、setInterval(fn, n)在每次把任务 push 到任务队列时,都要进行一下判断,看上次的任务是否仍在队列中,如果有则不添加,没有则添加

所以,关于文章开头的拿到趣味题目,通过上述浏览器线程的知识,就可以解释清楚了。

你可能感兴趣的:(javascript,javascript,前端)