Dive into downloads blocked by script

在Yahoo的一篇文章里看到如下描述:

The problem caused by scripts is that they block parallel downloads. The HTTP/1.1 specificationsuggests that browsers download no more than two components in parallel per hostname. If you serve your images from multiple hostnames, you can get more than two downloads to occur in parallel. While a script is downloading, however, the browser won’t start any other downloads, even on different hostnames.

In some situations it’s not easy to move scripts to the bottom. If, for example, the script usesdocument.write to insert part of the page’s content, it can’t be moved lower in the page. There might also be scoping issues. In many cases, there are ways to workaround these situations.

An alternative suggestion that often comes up is to use deferred scripts. The DEFER attribute indicates that the script does not contain document.write, and is a clue to browsers that they can continue rendering. Unfortunately, Firefox doesn’t support the DEFER attribute. In Internet Explorer, the script may be deferred, but not as much as desired. If a script can be deferred, it can also be moved to the bottom of the page. That will make your web pages load faster.

看到上述加粗的一句话,说明浏览器下载script的过程一定是单线程的,如果某个script的下载需要一段比较长的时间,那么整个页面的加载可能就会被大大拖慢。为什么不能并行下载呢?其中一个原因可能是如果后一个script需要使用前一个script的结果的话,那么最简单的解决方法是硬性规定下载顺序。

除了这点外,原文还说到一个要点,就是可以在JavaScript中加一个叫DEFER的属性。使用这个属性的前提有两点:

  1. 这个script里不能包含有立即改变网页内容的语句,例如document.write
  2. 不要script中包括任何立即执行脚本要使用的全局变量或者函数

defer型script具体写法是这样的:

<script language="javascript" type="text/javascript" defer>

<script type="text/javascript" defer="defer">

遗憾的是Firefox不支持此属性,而且IE在里也不是如你想象中解析这个属性,我在另一篇文章中看到如下描述:

Explorer 4+ on Windows has slightly changed the meaning ofdefer. Any code inside deferred script tags is only executed when the page has been parsed entirely.

那么在IE里就相当于window.onload了,也可能是这个原因,这个鸡肋的属性没有被广泛地应用。

到目前为止,我搜索到的普片适用的解决方法只有两个,一个是使用HTML5的asyncs属性,另一个是如Yahoo那篇文章所说,直接把script放在文档尾部

一个成功的在支持HTML5浏览器上实现并行下载script的例子是:

(function() {
    var s = document.createElement('script');
    s.type = 'text/javascript';
    s.async = true;
    s.src = 'http://yourdomain.com/script.js';
    var x = document.getElementsByTagName('script')[0];
    x.parentNode.insertBefore(s, x);
})();

关于script三种加载方式的时间顺序图:普通方式,使用defer属性,使用HTML5的async属性

普通方式:          也就是单线程方式
defer属性方式:简单地把执行时间推迟到HTML Parser完毕
async属性:      下载的同时可以进行HTML Parser的工作,而且当script下载完毕以后不用等待便可以立即执行

UPDATE: 在Firefox 3.6.13和IE8上只验证了不能从同一个IP中同时下载两个文件,但可以从不同IP中同时下载不同的scripts.

参考文章:
http://developer.yahoo.com/performance/rules.html
http://www.itlearner.com/article/4313
http://www.quirksmode.org/js/placejs.html
http://techie-buzz.com/webmaster-tips/load-banner-ads-after-page-loads.html
http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/