New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浏览器阻塞探究 #2

Open
ericdum opened this Issue Oct 24, 2013 · 2 comments

Comments

Projects
None yet
2 participants
@ericdum
Owner

ericdum commented Oct 24, 2013

上周发现虾米音乐的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到底怎么提升了速度过几天在看了。

@ghost ghost assigned ericdum Oct 24, 2013

@breath-co2

This comment has been minimized.

Show comment
Hide comment
@breath-co2

breath-co2 Oct 24, 2013

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 commented Oct 24, 2013

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跑死了,浏览器时钟就会停留在那。

@ericdum

This comment has been minimized.

Show comment
Hide comment
@ericdum

ericdum Oct 24, 2013

Owner

@breath-co2

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

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

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

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

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

Owner

ericdum commented Oct 24, 2013

@breath-co2

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

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

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

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment