Skip to main content

[JS] Async, defer attributes

TL;DR

  • defer 會讓 scripts 的檔案先開始被下載,但在 HTML 文件準備好後才開始執行,同時會確保各個 script 檔案執行的順序
  • async 會讓 scripts 的檔案先開始被下載,但它不會確保各個 script 檔案被執行的順序,先下載好的就先執行

在 HTML 中載入 JS 三種常用的方式

Imgur

只使用 script

Imgur

<!-- https://javascript.info/script-async-defer -->

<p>...content before script...</p>
<script src="demo.js"></script>

<!-- 當 JS 還沒下載與解析完前,HTML 的繪製會卡在這,因此需等到 script 載入完後,才會看到下面的內容 -->
<p>...content after script...</p>

執行到這段程式碼時,整個網頁的繪製會停下,等 demo.js 下載完並執行完後,網頁繪製才繼續。

defer

Imgur

<script src="demo1.js" defer></script>
<script src="demo2.js" defer></script>

網頁繪製不會停下, demo.js 在背景下載,不會阻塞頁面的繪製,並且會等到 DOM 準備好後(但 DOMContentLoaded 事件發生前)才加以執行。因此相當實用,且目前瀏覽器多已經支援

和傳統只使用 <script> 的方式一樣,使用 defer 能夠保證 scripts 的檔案會按照 document 中的順序執行,以上面的程式碼為例,demo1.jsdemo2.js 會同時才背景開始被下載,但即使 demo2.js 比較早下載完成,瀏覽器仍會確保 demo1.js 執行完後才執行 demo2.js

tip

defer 適合用在需要等待 DOM 完成後才能被執行的 JS,後者多個 JS 檔彼此之間有相依性的情況。

async

Imgur

<script src="demo1.js" async></script>
<script src="demo2.js" async></script>

defer 類似的是 demo1.jsdemo2.js 都會在背景下載,但不同的地方在於,async 會在該 JS 檔下載完成後,就會馬上被執行,它不會等待 DOM ready 後才執行,也不保證各 script 間執行的順序,也就是說,如果 demo2.jsdemo1.js 還快被下載完成的話,它就會先被執行。

因此,async 適合用在每個 script 的 JS 檔彼此之間沒有相依性,不需要誰先執行才能換另一個執行的這種情況(例如,Google Analytics)。

tip

async 適合用在各 script 間的 JS 檔沒有相依的情況下使用,例如 Ads、GA 等等。

defer 和 body script 的不同

傳統上,為了解決單純使用 script 會卡住瀏覽器繪製,以及 HTML 可能還沒完全繪製完,會取不到 DOM 的問題(例如,jQuery 取不到元素),因此會把 <script> 放到 <body> 的最後才執行,以避免上述提到的問題,像是這樣:

<body>
<!-- other html -->
<script src="demo.js"></script>
</body>

這麼做雖然看起來看 defer 的效果很像,但最大的差別是該 JS 檔開始被下載的時間點。如果是 defer 的話這隻 JS 檔很早就開始被「下載」,直到 DOM 完全建置好後(但會在 DOMContentLoaded 事件前)才被「執行」;但把 <script> 放到 <body> 的最後,則是要等這段語法被解析到時才會開始「下載」JS 檔,接著立即執行。