<script>并发

浏览器中script标签加载顺序是如何的呢?这个问题折腾了好几次了,之前弄清楚了以后,觉得做不做笔记的无关紧要,可是后来发现,随着的时间的推移,知识逐渐被遗忘。然而更悲催的是,下次再遇到同样的问题的时候,脑中仅仅有一个模糊的印象,一切又得重新开始,关键是每一次又要花掉不少时间。废话不多,开始正题。

从浏览器加载HTML文件开始说起,如果HTML文件中引用了外部脚步文件,比如script标签,那么它是什么时候加载这个script标签的呢?如果有多个script标签,是不是它们是按顺序加载的呢?后面script标签的加载会不会等待前面script标签加载完毕后才开始呢?。。。。那defer和async又如何呢?下面一 一来探讨。首先,浏览器在加载HTML文件的时候,并不是边加载边解析,它是得到一份完整的HTML文件后才开始解析,而script标签是在HTML文件解析的时候才加载外部文件,理由很简单,因为只有在解析时浏览器才知道某个标签是做什么用的。有图有真相,看下图。

这是主文件的截图(index.jsp):


脚本文件的截图(one.jsp):


另一个脚本文件的截图(two.jsp):


最后一个脚本文件(websocket.jsp):


现在,向index.jsp文件发送一个请求,控制台输出是(chrome):


说明了什么?说明了script标签加载文件的时候是在HTML文件解析开始后。不知道有没有人发现,one.jsp文件并没有阻塞two.jsp和websocket.jsp文件的加载。这是因为,新版本的浏览器可以同时并发多个http请求,而我的浏览器时最新版本的。看到这里,可能有人会认为script标签引用外部文件的加载是按照它们在文档中出现的顺序加载的,起初我也是这么认为的,但事实似乎不是这样。再次向index.jsp文件发送一个请求,结果如下图:


上面红色的1和2分别表示第一次和第二次请求,结果说明,script标签并不是严格按照它们在文档中出现的顺序来加载外部文件的。但是,它们确实是按照在文档中出现的顺序来执行的:


上述情况在其它浏览器中的情况如何呢?先来看看IE(IE11):


我试了多次,每次结果都一样,上面是其中两次的截图,结果似乎表明,IE中是严格按照顺序加载的,并且前面的script标签加载过程中会阻塞后面script标签的加载。

再来看看在火狐中的结果:


我试了几次,这是前两次的结果,似乎也表明并不是按照顺序加载的,而且one.jsp文件也并没有阻塞后面websocket.jsp的加载。可是接下来几次的结果都是这样:


它们又按照顺序加载了,虽然前面的文件依然没有阻塞后面文件的加载。IE会一直阻塞后面文件的加载。前面提到,新版本的浏览器可以同时并发多个http请求(据前面的结果,这一点在IE中似乎不是),那么,浏览器可以同时并发多少个请求呢?我们来试试。修改一下index.jsp文件,其中script标签如下:

其中websocket标签的内容为:


其他标签的内容都类似这样:


下面是在chrome中截图:


我试了几次,每次都是谁一样,从中可以看出,chrome中最大并发数为6。

下面再来看看IE中的结果:


与预期一样,IE一如既往的阻塞了后面脚本的加载。再来看看火狐:


还是与预期一样,火狐与chrome的表现类似,一样是六个并发。

下面来看看defer如何影响脚本的加载。javascript高级程序设计(第三版)中指出,defer会指示脚本立即加载,但会延迟到页面解析完毕后在执行,并且defer指定的script标签不会再影响页面的解析,言外之意是defer指定的标签即使加载过程中发生阻塞也不会阻塞后面标签的解析。我们来看看效果。先修改index.jsp中的文件,如下图:


另外监听在index.jsp中DOMContentLoaded事件:


其他文件的内容不变,现在来发生一个请求试试:


控制台执行结果

结果真是出乎意料,再一次请求试试:


控制台执行结果


变化不大,从上面的结果分析,defer指示的 script标签似乎是等到其他标签加载完成后再加载,即使同时的http请求还没达到最大并发数,同时,前后两次请求中,defer指示的标签一直是按出现的先后顺序加载的。还有就是,defer指示的标签在DOMContentLoaded事件前执行。

先来验证一下defer指示的标签是不是一直按照先后顺序加载的,我们来修改下index.jsp文件:


其他地方不变,发送一个请求看看:


控制台结果


看来并不是这样,加载还是不严格按照script标签在文档中的顺序。

把index.jsp文件修改为原样:


发送一个请求,看看在火狐中的结果:

控制台


结果当真是出乎意料,第一与chrome‘分道扬镳’,再来试一次:


控制台


还是一样。再来看看在IE中的结果:


控制台


见鬼,IE难得的出现了并发并且出现了6个并发(5个defer一个非defer),并且defer推迟到正常标签执行完毕后执行。我们来看看IE最大并发数是多少,修改index.jsp文件如下:


发送一个请求看看:

同样是6,并且也不再按照script标签出现的顺序加载了。前面提到,IE中正常script标签会阻塞后面正常script标签的加载,现在来看看它是否也阻塞defer指示的script标签的加载。修改index.jsp文件:


发送一个请求看看:


还是会阻塞defer指示的script标签。再来修改index.jsp文件看看:


发送一个请求:


并发数是5,还是一样,即使没达到最大并发数,正常script标签一样阻塞后面标签的加载。

现在来看看async是如何影响script标签的加载与执行的,修改index.jsp文件:

在 chrome中发生 一个请求:


控制台


在chrome中倒是与defer一样,同时,有两点不一样,一点是,async指示的script标签的执行在DOMContentLoaded事件之后,另一点是,即使四个async标签加载完成的先后顺序是:four.jsp、three.jsp、one.jsp、two.jsp,但它们的执行顺序却是:one.jsp、two.jsp、four.jsp、three.jsp,几乎无序可寻。

再来看看在火狐中的结果:


控制台


和chrome的差别很大,async指示的四个文件加载完成的先后顺序是:four.jsp、two.jsp、one.jsp、three.jsp,而他们执行的先后顺序是:four.jsp、two.jsp、one.jsp、three.jsp。加载完成顺序与执行顺序一致。但是,他们完全没有等到正常script标签执行就开始执行了。为了结果更可靠一些,再来次请求:


控制台


async指示的四个文件加载完成的先后顺序是:four.jsp、two.jsp、one.jsp、three.jsp,而他们执行的先后顺序是:four.jsp、two.jsp、one.jsp、three.jsp,加载完成顺序与执行顺序一致,并且加载完成就执行。

最后来看看IE,发送一个请求:


控制台十个文件执行顺序是:one.jsp、three.jsp、two.jsp、four.jsp、five.jsp、six.jsp、sevent.jsp、eight.jsp、nine.jsp、websocket.jsp、DOMContentLoaded事件。

从上述结果可以看出,IE和火狐对待async标签的方式差不多,不同的一点是,IE中async标签与chrome中一样,并不是按照下载完成顺序执行。再来看看IE会不会阻塞async标签的加载,修改index.jsp文件:


发送一个请求:


控制台十个文件执行顺序是:one.jsp、two.jsp、three.jsp、four.jsp、five.jsp、six.jsp、eight.jsp、seven.jsp、nine.jsp、websocket.jsp、DOMContentLoaded事件。

从结果可以看出,IE中正常标签同样阻塞了async标签的加载。async标签同样不按加载完成顺序执行。

总结一下,三个浏览器最大并发数为6。在chrome中先全部加载正常标签再加载defer和async标签(但正常标签加载的阻塞可能会阻塞其他标签的加载),同时,defer标签在DOMContentLoaded事件前执行,且按照script标签在文档中出现的顺序执行,async标签加载完毕就可能执行,不会等待正常标签的执行,无论是在DOMContentLoaded事件之前还是之后,且执行顺序与async标签加载完成先后顺序无关。除此之外,在chrome中在请求并发数没达到最大并发数时,所有标签都不会阻塞其他标签的加载。在火狐中,所有标签都是并发加载,正常标签不会阻塞defer标签和async标签的加载,但是defer标签会在正常标签执行完毕后再按在文档中出现的顺序执行。async标签则是加载完毕后就执行,而不会等待正常 script标签的执行,同时他们的加载顺序与他们在文档中出现的顺序也没关系。在IE中,正常标签加载的阻塞会阻塞其他在文档中出现在它后面的script标签的加载,无论是正常标签还是defer和async标签。defer和async标签加载的阻塞不会阻塞其他在文档中出现在它后面的script标签的加载,无论是正常标签还是defer和async标签。defer标签会推迟到正常标签执行完毕后再按在文档中出现的顺序执行,并且async标签的执行并不是按照其加载完成先后顺序,async标签的执行与chrome一样,无序可寻。无论是在火狐还是IE或者chrome中,defer和async标签都可以在DOMContentLoaded事件前执行,但只有async标签可以在DOMContentLoaded事件后执行。简单来讲,async标签加载完成后就可能执行且执行可能无序,而不会等待其他标签的执行,而defer则是加载完成后需要等待正常标签执行完毕后才会执行。defer标签和正常标签一定会在DOMContentLoaded事件之前执行完毕,而async标签可能会在DOMContentLoaded事件之前或之后执行。关于defer和async与DOMContentLoaded事件的关系,留待下一篇文章详细探讨。

下面的一段话摘取某篇博文:

Both async and defer scripts begin to download immediately without pausing the parser and both support an optional onload handler to address the common need to perform initialization which depends on the script. The difference between async and defer centers around when the script is executed. Each async script executes at the first opportunity after it is finished downloading and before the window’s load event. This means it’s possible (and likely) that async scripts are not executed in the order in which they occur in the page. The defer scripts, on the other hand, are guaranteed to be executed in the order they occur in the page. That execution starts after parsing is completely finished, but before the document’s DOMContentLoaded event.

摘自http://w3c.github.io/html/semantics-scripting.html#data-block

The async and defer attributes are boolean attributes that indicate how the script should be executed.Classic scripts may specify defer or asyncmodule scripts may specify async.

There are several possible modes that can be selected using these attributes, and depending on the script’s type.

For classic scripts, if the async attribute is present, then the classic script will be fetched in parallel to parsing and evaluated as soon as it is available (potentially before parsing completes). If the async attribute is not present but the defer attribute is present, then the classic script will be fetched in parallel and evaluated when the page has finished parsing. If neither attribute is present, then the script is fetched and evaluated immediately, blocking parsing until these are both complete.

For module scripts, if the async attribute is present, then the module script and all its dependencies will be fetched in parallel to parsing, and the module script will be evaluated as soon as it is available (potentially before parsing completes). Otherwise, the module script and its dependencies will be fetched in parallel to parsing and evaluated when the page has finished parsing. (The defer attribute has no effect on module scripts.)


classic script and module script:

The script element allows authors to include dynamic script and data blocks in their documents. The element does not represent content for the user.

The type attribute allows customization of the type of script represented :

Omitting the attribute, or setting it to a JavaScript MIME type, means that the script is a classic script, to be interpreted according to the JavaScript Script top-level production. Classic scripts are affected by the charsetasync, and defer attributes. Authors should omit the attribute, instead of redundantly giving a JavaScript MIME type.

Setting the attribute to an ASCII case-insensitive match for the string "module" means that the script is a module script, to be interpreted according to the JavaScript Module top-level production. Module scripts are not affected by the charset and defer attributes.

Setting the attribute to any other value means that the script is a data block, which is not processed. None of the script attributes (except type itself) have any effect on data blocks. Authors must use a valid MIME type that is not a JavaScript MIME type to denote data blocks.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 本文总结一下浏览器在 javascript 的加载方式。关键词:异步加载(async loading),延迟加载(...
    4ea0af17fd67阅读 1,025评论 0 2
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,490评论 18 139
  • 浏览器渲染页面的过程 从耗时的角度,浏览器请求、加载、渲染一个页面,时间花在下面五件事情上: DNS 查询 TCP...
    想做一个画家阅读 480评论 0 0
  • Tif_Lib阅读 461评论 0 1
  • 每个人都有一个不能说的忧伤 在这秋的夜 踏着朝圣者的脚步 叩问你的心腔 有一种殇 叫怕花谢的凄凉 有一种离别 叫不...
    汉儒应天阅读 140评论 0 0