目录
在了解async和defer的区别之前,我们需要先了解同步、异步和推迟的概念。
假如现在有一条非常狭隘的胡同,里面有两个人挨着走,那么现在请问后面的人能够跑快点走到前面那个人前面吗?肯定是不行的,因为路太窄了过不去。如果后面那个人比较急,而前面那个人又有腿伤跑不快呢,这就好比代码的阻塞情况。这两个人都被堵在了胡同里,那么这个胡同就像JS的执行引擎是主线程的一样,所有代码被限制在了一个主线程上,而这个正是 同步 的概念。
同步的阻塞是一种非常糟糕的情况,所以这时候我们可以再添加一条胡同,那么这两个人就可以各走各自的路,不需要在乎前面是否有人堵着自己过不去了,后面那个人像走到前面也就非常容易了,这个就是 异步 的概念。
虽然现在分出了两个胡同,但这两个人用的是一个出口,那么这两个人就会出现谁先出出口的问题。这个时候我们需要为异步增加一个条件,那就是后面的人必须等到前面的人出去后自己才能出去,这个便是 推迟 的概念。
异步的英语是async,推迟的英语是defer,同步的就不说了哈。但同步是Javascript中一个非常重要的概念。当浏览器加载HTML时一旦遇到<script>标签, 就会停下来先把<script>标签里的内容执行掉,如果<script>标签中有外部文件,那就必须等待下载和执行的步骤,这样浏览器才会继续向下加载,如果这个外部文件刚好在一个网络情况比较差的服务器上,这样整个网页的加载都会收到很大的影响,这就是同步带来的代码阻塞弊端。
async和defer都可以解决这个问题,先看下面这段代码:
<!-- HTML代码 -->
<body>
<script src="./index.js"></script>
<div>Hello world!</div>
</body>
// JS代码
console.log(document.querySelector("div"));
可以看到,在HTML的body标签中引入了JS文件,打印div元素。此时<script>标签是没有加 async 和 defer属性的,那么这个时候就会出现上面讲到的同步问题,不仅会阻塞了代码执行,还会出现JS脚本文件访问不到下面HTML代码的而出现打印结果为 null 的情况。但如果我们在<script>标签中加入了 async 或 defer 属性后,就不会出现这种代码阻塞的问题了,比如下面这种:
<body>
<script src="./index.js" async></script>
<div>Hello world!</div>
</body>
<!-- 或者 -->
<body>
<script src="./index.js" defer></script>
<div>Hello world!</div>
</body>
async:浏览器在加载页面的时候,如果遇到了async属性,浏览器就会立即进行下载,与此同时还会继续加载页面,这样就解决了阻塞的问题,虽然没有阻塞的情况,但是async下请求的脚本具体什么时候执行就说不定了,有时页面还没加载完就执行了,也有可能页面加载后才执行,因为这种不确定性,如果脚本是需要修改DOM的,那么就有可能会出错,因此,async比较适合一些第三方脚本。
defer: 浏览器加载时如果遇到defer属性,浏览器就会立即进行下载,与此同时还会继续加载页面,但相较于async的区别就来了,因为不管脚本是否下载完了, defer都会等待浏览器解析完了html之后再去执行脚本,因此defer比较适合与DOM操作有关联的脚本。
不管是async还是defer,两者都 只适用于外部脚本,且还需要注意兼容性问题(如果浏览器不能识别这两个属性,那还是把script标签内容放在页面底部比较好)
以上就是我对于async和defer的理解了,希望能够有所帮助!