async 和 defer 都是 HTML 4.01 中定义的 <script> 标签的属性,都被用于改变处理外部脚本的行为。一般来说,当浏览器解析到 script 标签时,会暂停 HTML 解析器,开始下载、解析、执行外部脚本,执行完成之后在继续解析 HTML。但是某些情况下,我们并不希望 JavaScript 的执行阻塞 HTML 的渲染,于是 defer 和 async 就派上用场了。

defer 属性

defer 属性的中文含义是『推迟执行』,用于表明脚本在执行时不会影响页面的构造,即脚本会被延迟到整个页面都执行完毕再执行。

理论上,具有 defer 属性的若干个脚本会按照在页面中出现的顺序依次执行,且会优先于 DOMContentLoaded 事件执行。但是,根据浏览器的具体实现,defer 脚本并不一定按顺序执行,也不一定先于 DOMContentLoaded 事件。因此同一个页面最好只有一个 defer 脚本。

async 属性

async 用于表示『异步执行』。与 defer 一样,async 外部脚本的下载不会阻塞 HTML 解析。与 defer 不同的是,具有 async 的若干个脚本的执行顺序是无法保证的,这依赖于网络条件,先下载完的先执行。因此需要确保 async 脚本直接不能相互依赖。另外,在异步脚本加载期间修改 DOM 也是不可取的。

可以确定的是,async 脚本一定会在 load 事件之前执行。

async and defer compare

对比

相同点

  1. 都用于改变脚本的处理行为
  2. 都会立即开始下载脚本,但不阻塞 HTML 解析
  3. 都只能用于处理外部脚本

不同点

deferasync
执行时机整个页面解析完毕后执行脚本下载完成后立即执行
规范要求先于 DOMContentLoaded,具体实现依赖浏览器不能确定,但一定先于 load 事件
执行顺序规范要求按出现先后顺序执行,具体实现依赖浏览器先下载完先执行
脚本个数单个页面最好一个 defer 脚本可以拥有多个 async 脚本

用法

考虑浏览器的兼容性,还是应该将外部脚本引用放在 </body> 之前。不过对于互相之间没有依赖的 JavaScript 外部脚本,可以利用 defer 和 async 的异步下载来优化页面展示时间。

因为 IE<=9 不支持 async,所以可以考虑同时使用 async 和 defer,以便在 async 不支持时启用 defer。

参考资料:

  1. 《JavaScript 高级程序设计》
  2. 浅谈script标签的defer和async

标签: none

添加新评论