<script>并發(fā)

瀏覽器中script標簽加載順序是如何的呢?這個問題折騰了好幾次了,之前弄清楚了以后,覺得做不做筆記的無關(guān)緊要,可是后來發(fā)現(xiàn),隨著的時間的推移,知識逐漸被遺忘。然而更悲催的是,下次再遇到同樣的問題的時候,腦中僅僅有一個模糊的印象,一切又得重新開始,關(guān)鍵是每一次又要花掉不少時間。廢話不多,開始正題。

從瀏覽器加載HTML文件開始說起,如果HTML文件中引用了外部腳步文件,比如script標簽,那么它是什么時候加載這個script標簽的呢?如果有多個script標簽,是不是它們是按順序加載的呢?后面script標簽的加載會不會等待前面script標簽加載完畢后才開始呢?。。。。那defer和async又如何呢?下面一 一來探討。首先,瀏覽器在加載HTML文件的時候,并不是邊加載邊解析,它是得到一份完整的HTML文件后才開始解析,而script標簽是在HTML文件解析的時候才加載外部文件,理由很簡單,因為只有在解析時瀏覽器才知道某個標簽是做什么用的。有圖有真相,看下圖。

這是主文件的截圖(index.jsp):


腳本文件的截圖(one.jsp):


另一個腳本文件的截圖(two.jsp):


最后一個腳本文件(websocket.jsp):


現(xiàn)在,向index.jsp文件發(fā)送一個請求,控制臺輸出是(chrome):


說明了什么?說明了script標簽加載文件的時候是在HTML文件解析開始后。不知道有沒有人發(fā)現(xiàn),one.jsp文件并沒有阻塞two.jsp和websocket.jsp文件的加載。這是因為,新版本的瀏覽器可以同時并發(fā)多個http請求,而我的瀏覽器時最新版本的。看到這里,可能有人會認為script標簽引用外部文件的加載是按照它們在文檔中出現(xiàn)的順序加載的,起初我也是這么認為的,但事實似乎不是這樣。再次向index.jsp文件發(fā)送一個請求,結(jié)果如下圖:


上面紅色的1和2分別表示第一次和第二次請求,結(jié)果說明,script標簽并不是嚴格按照它們在文檔中出現(xiàn)的順序來加載外部文件的。但是,它們確實是按照在文檔中出現(xiàn)的順序來執(zhí)行的:


上述情況在其它瀏覽器中的情況如何呢?先來看看IE(IE11):


我試了多次,每次結(jié)果都一樣,上面是其中兩次的截圖,結(jié)果似乎表明,IE中是嚴格按照順序加載的,并且前面的script標簽加載過程中會阻塞后面script標簽的加載。

再來看看在火狐中的結(jié)果:


我試了幾次,這是前兩次的結(jié)果,似乎也表明并不是按照順序加載的,而且one.jsp文件也并沒有阻塞后面websocket.jsp的加載。可是接下來幾次的結(jié)果都是這樣:


它們又按照順序加載了,雖然前面的文件依然沒有阻塞后面文件的加載。IE會一直阻塞后面文件的加載。前面提到,新版本的瀏覽器可以同時并發(fā)多個http請求(據(jù)前面的結(jié)果,這一點在IE中似乎不是),那么,瀏覽器可以同時并發(fā)多少個請求呢?我們來試試。修改一下index.jsp文件,其中script標簽如下:

其中websocket標簽的內(nèi)容為:


其他標簽的內(nèi)容都類似這樣:


下面是在chrome中截圖:


我試了幾次,每次都是誰一樣,從中可以看出,chrome中最大并發(fā)數(shù)為6。

下面再來看看IE中的結(jié)果:


與預期一樣,IE一如既往的阻塞了后面腳本的加載。再來看看火狐:


還是與預期一樣,火狐與chrome的表現(xiàn)類似,一樣是六個并發(fā)。

下面來看看defer如何影響腳本的加載。javascript高級程序設(shè)計(第三版)中指出,defer會指示腳本立即加載,但會延遲到頁面解析完畢后在執(zhí)行,并且defer指定的script標簽不會再影響頁面的解析,言外之意是defer指定的標簽即使加載過程中發(fā)生阻塞也不會阻塞后面標簽的解析。我們來看看效果。先修改index.jsp中的文件,如下圖:


另外監(jiān)聽在index.jsp中DOMContentLoaded事件:


其他文件的內(nèi)容不變,現(xiàn)在來發(fā)生一個請求試試:


控制臺執(zhí)行結(jié)果

結(jié)果真是出乎意料,再一次請求試試:


控制臺執(zhí)行結(jié)果


變化不大,從上面的結(jié)果分析,defer指示的 script標簽似乎是等到其他標簽加載完成后再加載,即使同時的http請求還沒達到最大并發(fā)數(shù),同時,前后兩次請求中,defer指示的標簽一直是按出現(xiàn)的先后順序加載的。還有就是,defer指示的標簽在DOMContentLoaded事件前執(zhí)行。

先來驗證一下defer指示的標簽是不是一直按照先后順序加載的,我們來修改下index.jsp文件:


其他地方不變,發(fā)送一個請求看看:


控制臺結(jié)果


看來并不是這樣,加載還是不嚴格按照script標簽在文檔中的順序。

把index.jsp文件修改為原樣:


發(fā)送一個請求,看看在火狐中的結(jié)果:

控制臺


結(jié)果當真是出乎意料,第一與chrome‘分道揚鑣’,再來試一次:


控制臺


還是一樣。再來看看在IE中的結(jié)果:


控制臺


見鬼,IE難得的出現(xiàn)了并發(fā)并且出現(xiàn)了6個并發(fā)(5個defer一個非defer),并且defer推遲到正常標簽執(zhí)行完畢后執(zhí)行。我們來看看IE最大并發(fā)數(shù)是多少,修改index.jsp文件如下:


發(fā)送一個請求看看:

同樣是6,并且也不再按照script標簽出現(xiàn)的順序加載了。前面提到,IE中正常script標簽會阻塞后面正常script標簽的加載,現(xiàn)在來看看它是否也阻塞defer指示的script標簽的加載。修改index.jsp文件:


發(fā)送一個請求看看:


還是會阻塞defer指示的script標簽。再來修改index.jsp文件看看:


發(fā)送一個請求:


并發(fā)數(shù)是5,還是一樣,即使沒達到最大并發(fā)數(shù),正常script標簽一樣阻塞后面標簽的加載。

現(xiàn)在來看看async是如何影響script標簽的加載與執(zhí)行的,修改index.jsp文件:

在 chrome中發(fā)生 一個請求:


控制臺


在chrome中倒是與defer一樣,同時,有兩點不一樣,一點是,async指示的script標簽的執(zhí)行在DOMContentLoaded事件之后,另一點是,即使四個async標簽加載完成的先后順序是:four.jsp、three.jsp、one.jsp、two.jsp,但它們的執(zhí)行順序卻是:one.jsp、two.jsp、four.jsp、three.jsp,幾乎無序可尋。

再來看看在火狐中的結(jié)果:


控制臺


和chrome的差別很大,async指示的四個文件加載完成的先后順序是:four.jsp、two.jsp、one.jsp、three.jsp,而他們執(zhí)行的先后順序是:four.jsp、two.jsp、one.jsp、three.jsp。加載完成順序與執(zhí)行順序一致。但是,他們完全沒有等到正常script標簽執(zhí)行就開始執(zhí)行了。為了結(jié)果更可靠一些,再來次請求:


控制臺


async指示的四個文件加載完成的先后順序是:four.jsp、two.jsp、one.jsp、three.jsp,而他們執(zhí)行的先后順序是:four.jsp、two.jsp、one.jsp、three.jsp,加載完成順序與執(zhí)行順序一致,并且加載完成就執(zhí)行。

最后來看看IE,發(fā)送一個請求:


控制臺十個文件執(zhí)行順序是:one.jsp、three.jsp、two.jsp、four.jsp、five.jsp、six.jsp、sevent.jsp、eight.jsp、nine.jsp、websocket.jsp、DOMContentLoaded事件。

從上述結(jié)果可以看出,IE和火狐對待async標簽的方式差不多,不同的一點是,IE中async標簽與chrome中一樣,并不是按照下載完成順序執(zhí)行。再來看看IE會不會阻塞async標簽的加載,修改index.jsp文件:


發(fā)送一個請求:


控制臺十個文件執(zhí)行順序是:one.jsp、two.jsp、three.jsp、four.jsp、five.jsp、six.jsp、eight.jsp、seven.jsp、nine.jsp、websocket.jsp、DOMContentLoaded事件。

從結(jié)果可以看出,IE中正常標簽同樣阻塞了async標簽的加載。async標簽同樣不按加載完成順序執(zhí)行。

總結(jié)一下,三個瀏覽器最大并發(fā)數(shù)為6。在chrome中先全部加載正常標簽再加載defer和async標簽(但正常標簽加載的阻塞可能會阻塞其他標簽的加載),同時,defer標簽在DOMContentLoaded事件前執(zhí)行,且按照script標簽在文檔中出現(xiàn)的順序執(zhí)行,async標簽加載完畢就可能執(zhí)行,不會等待正常標簽的執(zhí)行,無論是在DOMContentLoaded事件之前還是之后,且執(zhí)行順序與async標簽加載完成先后順序無關(guān)。除此之外,在chrome中在請求并發(fā)數(shù)沒達到最大并發(fā)數(shù)時,所有標簽都不會阻塞其他標簽的加載。在火狐中,所有標簽都是并發(fā)加載,正常標簽不會阻塞defer標簽和async標簽的加載,但是defer標簽會在正常標簽執(zhí)行完畢后再按在文檔中出現(xiàn)的順序執(zhí)行。async標簽則是加載完畢后就執(zhí)行,而不會等待正常 script標簽的執(zhí)行,同時他們的加載順序與他們在文檔中出現(xiàn)的順序也沒關(guān)系。在IE中,正常標簽加載的阻塞會阻塞其他在文檔中出現(xiàn)在它后面的script標簽的加載,無論是正常標簽還是defer和async標簽。defer和async標簽加載的阻塞不會阻塞其他在文檔中出現(xiàn)在它后面的script標簽的加載,無論是正常標簽還是defer和async標簽。defer標簽會推遲到正常標簽執(zhí)行完畢后再按在文檔中出現(xiàn)的順序執(zhí)行,并且async標簽的執(zhí)行并不是按照其加載完成先后順序,async標簽的執(zhí)行與chrome一樣,無序可尋。無論是在火狐還是IE或者chrome中,defer和async標簽都可以在DOMContentLoaded事件前執(zhí)行,但只有async標簽可以在DOMContentLoaded事件后執(zhí)行。簡單來講,async標簽加載完成后就可能執(zhí)行且執(zhí)行可能無序,而不會等待其他標簽的執(zhí)行,而defer則是加載完成后需要等待正常標簽執(zhí)行完畢后才會執(zhí)行。defer標簽和正常標簽一定會在DOMContentLoaded事件之前執(zhí)行完畢,而async標簽可能會在DOMContentLoaded事件之前或之后執(zhí)行。關(guān)于defer和async與DOMContentLoaded事件的關(guān)系,留待下一篇文章詳細探討。

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

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?async;?module 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?charset,?async, 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.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內(nèi)容

  • 本文總結(jié)一下瀏覽器在 javascript 的加載方式。關(guān)鍵詞:異步加載(async loading),延遲加載(...
    4ea0af17fd67閱讀 1,066評論 0 2
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 瀏覽器渲染頁面的過程 從耗時的角度,瀏覽器請求、加載、渲染一個頁面,時間花在下面五件事情上: DNS 查詢 TCP...
    想做一個畫家閱讀 490評論 0 0
  • Tif_Lib閱讀 471評論 0 1
  • 每個人都有一個不能說的憂傷 在這秋的夜 踏著朝圣者的腳步 叩問你的心腔 有一種殤 叫怕花謝的凄涼 有一種離別 叫不...
    漢儒應天閱讀 150評論 0 0