async vs defer 的区别

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: async vs defer 的区别

1、why 两者

从事前端的小伙伴应该都知道,浏览器的渲染引擎负责解析服务器返回的一些 html、css 等资源,这些资源在未被解析之前都是以文本的形式存在,需要浏览器将对应的 html、css 解析成对应的 DOM、CSSOM 树,然后会将 DOM 和 CSSOM 合成为一棵 render 树,再经过 布局、绘制、合成 三个步骤将页面最总显示出来。


有一个问题,就是在浏览器解析 html 的过程中,如果遇到 <script> 标签的时候,浏览器就会停止解析 html,转而去执行 <script> 标签中的脚本,如果脚本中含有网络请求,那就会等网络请求完后再执行脚本中的代码,然后再回头继续解析 html。这样就造成了 html 解析被阻塞,从而导致页面渲染变慢。更严重是,如果在脚本中网络请求的资源恰好在一台网络情况较差的服务器上,这样整个网页的加载都会收到很大的影响。


举个例子:如果页面的 html 中,嵌入了一个 <script src="a.js"></script> 的标签,浏览器在解析到该便签时主要会做以下几个步骤:1. 停止解析 html、2.执行 a.js 中的脚本、3.如果 a.js 中有其他资源,则继续请求并执行、4.继续解析 html,由此就阻碍了 html 的解析。


为了针对上述的问题,就出现了 async 和 defer 两种解决方式。首先简要介绍一下:


两者都会并行下载 js,不会影响页面的解析,不同的是:


defer 会按照顺序在 DOMContentLoaded 前按照页面出现顺序依次执行脚本。

async 则是下载完立即执行。

如果遇到 js 脚本中有 document.write(),则在上述两者的使用时,浏览器会发出警告。

2、async

对于普通脚本,如果存在 async 属性,那么普通脚本会被并行请求,并尽快解析和执行。

对于模块脚本,如果存在 async 属性,那么脚本及其所有依赖都会在延缓队列中执行,因此它们会被并行请求,并尽快解析和执行。

该属性能够消除解析阻塞的 Javascript。举个例子:

<script src="b.js"async></script>
<script src="c.js"async></script>
  • 不阻止解析 document, 并行下载 b.js,c.js
  • 当脚本下载完后立即执行,两者执行顺序不确定,执行阶段不确定,可能在 DOMContentLoaded 事件前或者后

3、defer

这个布尔属性被设定用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。

defer 属性的脚本会阻止 DOMContentLoaded 事件,直到脚本被加载并且解析完成。举个例子:

<script src="d.js" defer></script>
<script src="e.js" defer></script>
  • 不阻止解析 document, 并行下载 d.js,e.js
  • 即使下载完 d.js, e.js 仍继续解析 document
  • 按照页面中出现的顺序,在其他同步脚本执行后,DOMContentLoaded 事件前 依次执行 d.js,e.js

4、总结

<script src="script.js"></script>:没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即“ 指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。


<script async src="script.js"></script>:有 async,在加载和渲染后续文档元素的过程时,将和 script.js 的加载与执行并行进行(异步),如果脚本中涉及操作 DOM 的操作就可能出现问题(DOM 还没解析完成)。


<script defer src="myscript.js"></script>:有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DoMContentLoaded 事件触发之前完成,因此 defer 适合与 DOM 有关联的脚本。


注意:


如果 script 无 src 属性,则 defer, async 会被忽略

动态添加的 script 标签隐含 async 属性

不管是 async 还是 defer,两者都只适用于外部的脚本,而且还要注意兼容性的问题,如果浏览器不能兼容,还是把 script 标签放到页面的底部比较好。

目录
相关文章
|
6月前
|
前端开发 UED
【面试题】async/await 函数到底要不要加 try catch ?
【面试题】async/await 函数到底要不要加 try catch ?
|
2月前
|
JavaScript 前端开发 Go
async 和 defer的作用与区别
async 和 defer的作用与区别
|
6月前
|
前端开发 JavaScript 开发者
阿珊带你深入理解 async/await 函数
阿珊带你深入理解 async/await 函数
|
2月前
|
前端开发 JavaScript
async和await的错误处理
在TypeScript中,`async`和`await`是处理异步操作的语法糖,能够让异步代码看起来像同步代码。`async`函数返回的总是`Promise`对象。当`await`后跟的表达式为`reject`状态的`Promise`时,会抛出错误,需要通过`try/catch`、链式`.catch()`或外部`Promise`包装来处理错误,防止代码崩溃。
104 0
async和await的错误处理
|
3月前
|
C#
C# async await 异步执行方法
C# async await 异步执行方法
54 0
|
6月前
|
前端开发 UED
【面试题】 async/await 函数到底要不要加 try catch ?
【面试题】 async/await 函数到底要不要加 try catch ?
【面试题】 async/await 函数到底要不要加 try catch ?
|
6月前
|
JSON 前端开发 JavaScript
async/await语法
async/await语法
56 0
如何给所有的 await async 函数添加try/catch?
如何给所有的 await async 函数添加try/catch?
|
前端开发 JavaScript
async、await 实现原理
async、await 实现原理
82 1
|
前端开发 JavaScript
从 async 和 await 函数返回值说原理
昨天在文章《JavaScript中的 async 和 await》中简单的介绍了其使用方法。那么 async 和 await 如何工作,如何正确的使用 async 和 await 。
391 0