mujiang.info icon indicating copy to clipboard operation
mujiang.info copied to clipboard

浏览器阻塞探究

Open ericdum opened this issue 10 years ago • 2 comments

上周发现虾米音乐的js都放在head里面,心想不是会卡进程么?和虾米音乐的同学聊了之后发现他们认为这个网站功能和js密切相关,所以不希望让用户看到不可用的页面。

根据产品选择技术可能无可厚非,其他优化方案这里不谈,我就想看看卡渲染进程到底是怎么卡的。

所以有了以下思路,

  1. 在head中,用while去无限循环跑3秒。
  2. 进入body,输出
  3. 再用同样的方式跑3秒,输出
  4. 外调一个3秒网络延时的js,同时执行其他js
  5. 同时外调一个5秒、10秒网络延时的js,同时执行其他js

期望:

  1. 无限循环js卡住进程导致白屏
  2. 上一步结束后输出
  3. body中的无限循环再次卡住进程,证明head、body无异
  4. 网络进程依次加载js、执行js
  5. 总执行时间3+3+3+5+10 = 24秒

循环代码:

    var time1 = (new Date()).getTime();
    while(((new Date()).getTime()-time1) < 3000);

加载外部js,并即时的代码:

    <script>
        var time = (new Date()).getTime();
        var intv = setInterval(function(){ timer3.innerText = ((new Date()).getTime()-time)/1000 }, 50);
    </script>
    <script onload="clearInterval(intv);" src="http://mujiang.info/delayload/3s.php"></script>

开始测试

查看测试、代码: http://cdpn.io/ABnmI

结果:

qq20131024-1

safari和chrome都类似。但是呈现的顺序不一样。chrome等到两段卡3秒的循环都结束了才呈现。但safari第一段js结束就开始呈现了。

可以看到外调js种,5秒、10秒的测试之用了7秒左右的时间。再去查看timeline,原来第一个三秒的js在请求的同时就开始请求5秒、10秒的js了。

所以来看看完成的期望:

  1. 无限循环js卡住进程导致白屏
  2. body中的无限循环再次卡住进程,证明head、body无异

没完成的是:

  1. 上一步结束后输出 —— 浏览器结果不一致
  2. 网络进程依次加载js、执行js —— 同时加载
  3. 总执行时间3+3+3+5+10 = 24秒 —— 总时间位3+3+3+7 = 16秒,不过这是好事。

至此,又有了几个新的思考:

  1. 既然所有js同时加载,为什么不在一开头就加载呢?(前面有6秒等候时间)。
  2. HTML5 的 async属性能不能防止网络阻塞,执行顺序和加载顺序一样吗。
  3. HTML4 的 defer属性能不能防止执行阻塞。

回答思考

这是刚开在chrome中的Network视图,看着3个js加载之前长长的空白,不觉得蛋微微有点疼么?浪费可耻啊!那6秒都干了些什么!

qq20131024-2

解决方案很简单,很容易观察出来第一段js加载的时候,其他的js就都跟着加载了。

那么我在head最开始就加一段js会怎么样呢?

效果:http://mujiang.info/test/browserjam/index2.php

可以看到执行时间被压到了10秒,快乐整整6秒,就是刚才前面卡的6秒。(口水都流出来了)

Network: (多出来的js就是刚加的js)

qq20131024-3

至于浏览器同学一次能同时加载多少个文件呢?safari、chrome都是6个。其他浏览器没试。

qq20131024-6

话说加载图片会不会占线程呢?答案是会。总数就是6个。就不给图了

async的问题就更简单了,async加载的js不仅不会阻塞解析,还不会阻塞其他js的执行。

效果:http://mujiang.info/test/browserjam/async.php

asd1

至于defer属性,看了文档说被标记的js会在页面解析(parsing)完成之后执行。按照我的理解,浏览器parsing完之后还有构建DOM、构建render tree、layout和parinting的过程。所以如果真的只是不影响parsing的话,那就能解释结果为什么观察不出结果:http://mujiang.info/test/browserjam/defer.php

总结一下

  1. 大量的代码放在前面会导致“白屏”
  2. 当浏览器遇到第一个script的时候会把所有script都拿来下载。
  3. 同时下载的进程为6个。图片、js共享。
  4. async可以避免阻塞。但是js执行顺序为改变。

本文用到的代码都在这里: https://github.com/ericdum/mujiang.info/tree/master/test/browserjam

好吧,我偏题了,没测出safari有啥好的,除了渲染更新更快(也蛮好的)。不过这些东西我觉得比safari得自身情况更有意思。safari到底怎么提升了速度过几天在看了。

ericdum avatar Oct 24 '13 04:10 ericdum

8错,测试的很详细。但我觉得你测试的代码的概念有点问题。 首先,要知道js是单线程运行的,虽然有什么setTimeout之类的东西可以让js异步操作,但是并不代表是多线程。

接下来我来说下你的测试代码

var time1 = (new Date()).getTime();
while(((new Date()).getTime()-time1) < 3000);

这个东西并不是阻塞,而是把js跑死。阻塞和把线程跑死是两码事。 比如:

for (var i=1; i<10; i++)
{
   console.log(i);
   console.log(‘test’);
}

for (var i=1; i<10; i++)
{
   setTimeout(function(){
   console.log(i);
   }, 0);
   console.log('test');
}

这2个,前者输出就必定是先输出i再输出字符串,而后后者就不一样了

所以可以很简单的判断,不管是外部js还是内部js,甚至是异步加载的js,只要跑死了脚本(比如需要几秒钟才跑结束一个函数),后面所有的js进程都进步来的,因为是单线程。

至于渲染的问题,每个浏览器的表现是会有差异。因为渲染线程肯定和js不是一个线程的。 由于js进程卡住导致浏览器时钟停留在那边是很正常的一件事情。而浏览器渲染的策略不同也就造成了显示有差异,但不管怎么样,还是一句话:js跑死了,浏览器时钟就会停留在那。

breath-co2 avatar Oct 24 '13 07:10 breath-co2

@breath-co2

你说的是如何避免阻塞,我这里讨论的就是怎么阻塞。。死循环跑够时间就是为了模拟和放大大量的代码带来的cpu的负荷,才有利于观察。

浏览器解析流程中,当解析到了script又没有被标记成defer就会暂停解析,下载和执行script执行结束之后才会继续解析。

当页面解析完成,又会把defered的script拿来执行。

说白了,这个demo就是为了阻塞。

另外,js虽然是单线程但只是执行线程是单线程。设置timeout或者interval之后就编程了伪多线程。所以就不会阻塞了。

ericdum avatar Oct 24 '13 07:10 ericdum