解析JavaScript異步加載

本篇文章給你們分享了做者在學習JavaScript異步加載中遇到的問題,總結後給出了詳細的處理方案,寫的十分的全面細緻,具備必定的參考價值,對此有須要的朋友能夠參考學習下。若有不足之處,歡迎批評指正。javascript

同步加載的問題html

默認的js是同步加載的,這裏的「加載」能夠理解成是解析、執行,而不是「下載」,在最新版本的瀏覽器中,瀏覽器對於代碼請求的資源都是瀑布式的加載,而不是阻塞式的,可是js的執行老是阻塞的。這會引發什麼問題呢?若是個人index頁面要加載一些js,可是其中的某個請求遲遲得不到響應,因而阻塞了後面的js代碼的執行(同步加載),同時頁面渲染也不能繼續(若是js引入是在head標籤後)。前端

<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>

<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

this is a test
複製代碼

好比上面的這段代碼,保存爲index.html文件,頁面的主體是一個簡單的字符串,可是代碼執行後頁面遲遲都是空白,爲什麼?由於請求的js遲遲沒法加載(可能因爲谷歌被牆等緣由),因而阻塞了後面的代碼的執行,頁面得不到渲染。可能你會提議,把js代碼放到前不就能先渲染頁面了!好方法,咱們嘗試着將js放後面:html5

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
複製代碼

頁面瞬間被渲染,「this is a test"也很快出如今前臺,世界彷佛平靜了,但是:java

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
 console.log('hello world');
</script>//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860
複製代碼

在前面代碼的基礎上簡單加了一段代碼,可是"hello world"遲遲沒法在控制檯輸出,顯然前面的js請求阻塞了後面代碼的加載,咱們恍然大悟,改變js的加載位置只能改變頁面的渲染,然而對於js的加載並無什麼卵用,js仍是會阻塞。jquery

實現js異步加載程序員

咱們的要求彷佛很簡單,能在頁面加載的同時,在控制檯輸出字符串便可,再講的通俗一點,就是在請求第一段谷歌提供的js的同時,繼續執行下面的js,也就是實現js的異步加載。 最多見的作法是動態生成script標籤:面試

<body>
 this is a test
 <script type="text/javascript">
  ~function() {
   var s = document.createElement('script');
   s.src = 'http://china-addthis.googlecode.com/svn/trunk/addthis.js';
   document.body.appendChild(s);
  }();//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860
 </script>
 <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
 <script type="text/javascript">
  console.log('hello world');
 </script>
</body>
複製代碼

可是仍是有點問題,這種加載方式在加載執行完以前會阻止 onload 事件的觸發,而如今不少頁面的代碼都在 onload 時還要執行額外的渲染工做等,因此仍是會阻塞部分頁面的初始化處理:瀏覽器

<body>
 this is a test
 <script type="text/javascript">
  ~function() {
   // function async_load() {
    var s = document.createElement('script');
    s.src = 'http://china-addthis.googlecode.com/svn/trunk/addthis.js';
    document.body.appendChild(s);
   // }//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860
   // window.addEventListener('load', async_load, false);
  }();
 
  window.onload = function() {
   var txt = document.createTextNode(' hello world');
   document.body.appendChild(txt);
  };//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860
 </script>
 <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
</body>
複製代碼

好比上面的代碼不能很好地渲染」hello world」,咱們只需將註釋去掉就能夠了,讓谷歌提供的js在onload 時纔開始異步加載。這樣就解決了阻塞 onload 事件觸發的問題。bash

補充DOMContentLoaded 與 OnLoad 事件 DOMContentLoaded : 頁面(document)已經解析完成,頁面中的dom元素已經可用。可是頁面中引用的圖片、subframe可能尚未加載完。 OnLoad:頁面的全部資源都加載完畢(包括圖片)。瀏覽器的載入進度在這時才中止。這兩個時間點將頁面加載的timeline分紅了三個階段。 以上彷佛能較好解決這個問題,可是html5提供了更簡便的方法,async屬性!

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' async='async'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
 console.log('hello world');
</script>//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860
複製代碼

async是html5的新屬性,async 屬性規定一旦腳本可用,則會異步執行(一旦下載完畢就會馬上執行)。 須要注意的是async 屬性僅適用於外部腳本(只有在使用 src 屬性時) defer屬性經常和async一塊兒提起:

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer='defer'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
 console.log('hello world');
</script>//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860
複製代碼

彷佛實現效果差很少,可是真的同樣嗎?咱們來看看defer屬性的定義。 之前的defer只支持ie的hack,如今html5的出現開始全面支持defer。defer 屬性規定當頁面已完成加載後,纔會執行腳本。defer 屬性僅適用於外部腳本(只有在使用 src 屬性時)。ps:ie支持的defer彷佛並不是如此,由於對ie無感,不深究,有興趣的能夠去查閱相關資料。

既然async和defer常常一塊兒出現,那麼辨析一下吧! 若是沒有async和defer屬性(賦值爲true,下同),那麼瀏覽器會當即執行當前的js腳本,阻塞後面的腳本;若是有async屬性,加載和渲染後續文檔元素的過程將和當前js的加載與執行並行進行(異步);若是有defer屬性,那麼加載後續文檔元素的過程將和 script.js 的加載並行進行(異步),可是 script.js 的執行要在全部元素(DOM)解析完成以後,DOMContentLoaded 事件觸發以前完成。

藍色線表明網絡讀取,紅色線表明執行時間,這倆都是針對腳本的;綠色線表明 HTML 解析。 此圖告訴咱們如下幾個要點(摘自defer和async的區別):

  • defer 和 async 在網絡讀取(下載)這塊兒是同樣的,都是異步的(相較於 HTML 解析)
  • 它倆的差異在於腳本下載完以後什麼時候執行,顯然 defer 是最接近咱們對於應用腳本加載和執行的要求的
  • 關於 defer,此圖未盡之處在於它是按照加載順序執行腳本的,這一點要善加利用
  • async 則是一個亂序執行的主,反正對它來講腳本的加載和執行是牢牢挨着的,因此無論你聲明的順序如何,只要它加載完了就會馬上執行
  • 仔細想一想,async 對於應用腳本的用處不大,由於它徹底不考慮依賴(哪怕是最低級的順序執行),不過它對於那些能夠不依賴任何腳本或不被任何腳本依賴的腳原本說倒是很是合適的,最典型的例子:Google Analytics

可是在我看來(如下我的理解,若有出入還望指出),defer在異步加載上的應用並不會比async廣。async的英文解釋是異步,該屬性做用在腳本上,使得腳本加載(下載)完後隨即開始執行,和動態插入script標籤做用相似(async只支持h5,後者能兼容瀏覽器);而defer的英文解釋是延遲,做用也和字面解釋相似,延遲腳本的執行,使得dom元素加載完後纔開始有序執行腳本,由於有序,因此會帶來另外一個問題:

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer='defer'></script>//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js' defer='defer'></script>
<script type="text/javascript" src='index.js' defer='defer'></script>
console.log('hello world');
複製代碼

若是執行這段代碼,控制檯的「hello world」也會遲遲得不到結果。因此我以爲仍是async好用,若是要考慮依賴的話,能夠選擇requirejs、seajs等模塊加載器。

JavaScript的異步加載還有一些方式,好比:AJAX eval(使用AJAX獲得腳本內容,而後經過eval(xmlhttp.responseText)來運行腳本)、iframe方式等。

結語

感謝您的觀看,若有不足之處,歡迎批評指正。

爲了幫助你們讓學習變得輕鬆、高效,給你們免費分享一大批資料,幫助你們在成爲全棧工程師,乃至架構師的路上披荊斬棘。在這裏給你們推薦一個前端全棧學習交流圈:864305860 歡迎你們進羣交流討論,學習交流,共同進步。

當真正開始學習的時候不免不知道從哪入手,致使效率低下影響繼續學習的信心。

但最重要的是不知道哪些技術須要重點掌握,學習時頻繁踩坑,最終浪費大量時間,因此有有效資源仍是頗有必要的。

最後祝福全部遇到瓶疾且不知道怎麼辦的前端程序員們,祝福你們在日後的工做與面試中一切順利。

相關文章
相關標籤/搜索