######20190429更新
来源: https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html
图示:
绿色表示HTML解析过程,灰色表示HTML解析过程暂停,紫色表示脚本下载过程,粉色表示脚本执行过程。
没有defer和async的情况:
当使用script标签引入任意一个脚本文件,并且不带任何的属性(没有defer,也没有async)的时候页面渲染情况:
-
首选是HTML渲染
-
然后遇到script标签,开始下载脚本文件,同时HTML的解析工作被暂停
-
JS脚本下载完,这个时候HTML的解析工作仍然是暂停状态
-
JS脚本开始执行,这个时间内HTML的渲染也还是暂停的
-
JS脚本执行完毕,被暂停的HTML解析开始恢复
可以看到一般情况下,JS的下载和解析都让HTML的解析暂停,HTML的解析行为被JS的下载和执行两个过程中断。
只有async的情况:
当有async的时候页面渲染情况:
-
首先是HTML渲染解析
-
然后遇到script引入的JS文件,这个时候脚本开始下载,同时HTML仍然继续解析,这个和前面的情况不同
-
JS脚本文件下载完成,在JS脚本下载完成前,HTML的解析一直进行
-
JS脚本开始执行,因为浏览器是单线程的,当JS在执行的时候,HTML的解析就被暂停了
-
JS的执行完成了,被暂停的HTML解析恢复
只有defer的情况:
当有defer的时候页面渲染情况:
-
首先是HTML渲染解析
-
然后是遇到script引入的JS文件,这个时候JS脚本开始下载,和async一样,HTML的解析不受影响
-
JS的下载完成了,但是并不是立即开始执行,而是继续执行HTML的解析
-
HTML解析完成
-
最后JS脚本执行
综上所述,无论是async还是defer,都有一个异步下载的过程,脚本下载可以和HTML解析同步进行。唯一的区别是async会立即执行下载完的脚本,而defer则是等到HTML解析完毕再执行。
async和defer使用的一些规则:
-
如果JS脚本文件是一个独立模块没有依赖关系,那么可以使用async
-
如果JS脚本文件有依赖关系,被其他JS文件依赖或则依赖其他JS文件,则使用defer
-
如果JS脚本文件不大,然后本身依赖其他的async标注的脚本,可以直接以内联方式写脚本内容,而不用再通过async方式引入
#### 以下是以前的内容
对于熟知或者稍微了解javascript的人来说,或多或少的都听过同步加载和异步加载。说到同步加载和异步加载就不得不提及这么两个单词:
defer
async
这两个单词也会时常在一些网站源码出现。defer本身的含义就是推迟,延期,async则是异步的意思。我们来看几个例子:
<script src="script.js"></script> <script async src="script.js"></script> <script defer src="script.js"></script>
以上三个都是加载script这个JS文件。
第一条没有defer和async,表示浏览器读取到这段代码的时候会立即加载并执行脚本内的内容。这里的立即也就是不等这个script后面的文档元素加载进来就执行它了。
第二条,只有async,字面意思就是异步的,所以script.js文件肯定是异步加载了。这里的异步其实是同时并行处理的意思。页面加载到这个script.js之后一边加载一边执行这个脚本,然后同时又继续加载之后的文档元素并渲染。
第三条,只有defer,字面意思是延迟,所以script.js肯定是延迟加载?延迟执行?实际defer情况下,也是一种异步加载的方式,只不过不再是同时并行处理。只是script.js的加载和后面的元素加载是同时进行,然后等后面的元素全部加载完毕渲染完毕了,script.js才去做执行。
下面是加载渲染执行时间的对比图:
蓝色是JS网络下载时间,绿色是JS解析时间,红色是JS执行时间。
从上面可以看到:
-
只有defer和async存在并行处理的情况,而且是在资源下载和页面解析的情况下。也就是一边下载页面一边也在解析页面内容。
-
defer和async的唯一区别是红色部分,执行时间。defer延迟执行,等页面全部加载渲染完了再执行。
-
async的执行时间是在网络下载完了之后,不管顺序如何,一旦JS下载完了就立即执行。
-
async不考虑JS之间的依赖关系完全自由独立,典型的例子是谷歌和百度等统计代码。