一文教会你async和defer

写之前先摆出结论

async: 异步加载下载js脚本文件,在加载下载完之后立马执行js脚本文件.多个async脚本文件执行时执行顺序没有保障.执行js过程中,会阻塞html的解析和渲染

defer: 异步加载下载js脚本文件,在加载下载完之后等到html解析完毕才执行js脚本文件.多个defer脚本文件执行时的执行顺序有保障.执行js过程中,会阻塞html的解析和渲染

async和defer的共同作用:

  • 加载和下载js的时候不会阻塞html的解析和渲染

二者的区别

  • 执行的时间不同,async在下载或加载完成之后直接执行js脚本文件,但是defer会等到html完全的解析之后才会执行相对应的js文件. (注意点:在js文件执行的时候会阻塞html的解析和渲染))
  • 如果存在多个async和defer的时候,defer的执行顺序是有保障的,async的执行顺序是没保障的.async的执行顺序是谁先加载或下载完毕谁先执行.不讲求先来后到的规矩.但是defer不同,它还是讲先来后到的关系.

补充:如果想要使用DOM最好使用defer,因为它在html解析完全之后才执行.而async的执行时间没有保障.它可能在html解析完全之前就执行了.不能获取到所有的DOM


下面开始实操

实操前的准备

工具: 谷歌浏览器. vscode
打开谷歌浏览器的F12 将磁盘缓存关闭,将下载速度调成slow 3G
这里通过使用外部引入jQuery和echarts的cdn来模拟外部引入js文件,模拟js文件的下载

Snipaste_2023-06-21_10-18-56.png

首先来探究不加defer和async的时候.js的执行

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>

    <script src="https://code.jquery.com/jquery-3.7.0.js" ></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.2/echarts.min.js" ></script>
</head>
<body>
    <div>1</div>
</body>
</html>

首先要知道解析html是从头至尾进行解析的,浏览器会先处理head中的数据,在处理body中的数据.

所以浏览器一开始会接触到head中的script.浏览器会下载或加载js脚本文件.这个时候它既不是defer 的也不是async的.所以它下载和加载的时候会阻塞html的解析和渲染.

这个时候来看浏览器的瀑布图

Snipaste_2023-06-21_10-30-26.png

发现,在jquery和echarts下载完毕之后.html的解析才陆陆续续的开始.

补充: 这个时候你打开浏览器的直观感受就是: 浏览器图标一直转,浏览器有一段白屏的时间.


这个时候我们引入defer或者async我们再看看效果.

  • defer

Snipaste_2023-06-21_10-35-37.png

  • async

Snipaste_2023-06-21_10-37-02.png

这里我们可以发现,在加载或下载js脚本文件的时候,浏览器可以正常的解析html.

这个时候浏览器的直观显示是,页面很快就出来了,不会有白屏.但是浏览器的图片一直会转(在下载和加载js脚本文件)


然后我们在探究defer和async的区别

  • defer在html解析完之后执行,async在下载或加载完js脚本之后立马执行.

这里我们通过使用以下代码来完成实验

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>

    <script src="./_1.js" defer></script>
    <script src="./_2.js" async></script>
    <script src="https://code.jquery.com/jquery-3.7.0.js" ></script>
</head>
<body>
    <div>1</div>
    <div>222222</div>
</body>
</html>
    _1.js中是一句 console.log(1);   
    _2.js中是一句 console.log(2);
  1. 这里我们用到了之前的结论,不加defer和async的js脚本在加载或下载的时候会阻塞html的解析和加载.
  2. 而我们添加了defer的js脚本,浏览器碰到它会直接下载或加载它,但是它的执行会等到html都解析完全之后.
  3. 我们在这个例子里通过使用在head中使用一个不加defer和async的比较大的js脚本文件来模拟阻塞html的解析的过程.然后判断添加了defer的js脚本是否在html渲染完全之后再执行.(如果是添加了defer的脚本的执行时间会有延迟)
  4. 之前提及添加了async的脚本会在脚本加载和下载之后立即执行.所以无论你html是否被阻塞,都跟他的执行无关.所以它的执行会比之前添加了defer的快了很多.

控制台中显示的结果是

Snipaste_2023-06-21_11-02-58.png

控制台很快的输出2,然后过了一段时间才输出1

原因: _1.js加载虽然在_2.js之前.但是_1.js的执行要在html解析之后,这个时候html的解析被另一个不加async和defer的脚本给阻塞了,所以执行会比_2.js慢很多.然后_2.js虽然加载在_1.js之后,但是它加载完立马执行了不会受到另一个脚本的影响.所以整体来说它的执行速度比_1.js的快很多.


async和defer的区别的另一点

  • 多个defer执行顺序是有保证的,讲究先来后到,async是无保障的,谁先加载下载完就谁先执行.

这里使用两个js脚本文件来探究

// _1.js
for(let i = 0; i < 20000000000; i++) {}
console.log(1)

_2.js
console.log(2)
  • 使用async

Snipaste_2023-06-21_11-20-17.png

Snipaste_2023-06-21_11-24-58.png

你会发现_2执行在_1之前.这个就是表明了多个async执行时候的执行顺序,谁先加载下载完谁就先执行.

  • 再来看看defer

defer是讲究先来后到的规矩的,所以应该是显示_1.js执行,虽然它很慢.然后才是_2.js执行.所以输出的顺序应该显示1,然后才是2.

Snipaste_2023-06-21_11-28-45.png

Snipaste_2023-06-21_11-29-02.png

结果和我们预料的一样.


总结

  • 首要要搞清楚 加载下载执行 的区别. 浏览器碰到js文件都会先 加载和下载 js文件, 执行 js文件是要先判断是否有defer和async. 如果有defer的话,等到html解析完之后再进行执行. 如果是async, 下载加载 完之后立马 执行.
  • async 和 defer的 加载下载 是异步的,不会阻塞html的解析.如果不添加defer和async的js脚本文件,它的加载下载 会阻塞html的解析.
  • defer和async 执行 的特性决定了一些使用的场景,添加了async不适合用于操作DOM,如果想操作DOM,最好使用defer
  • 多个async的执行顺序是不固定的,谁先下载完谁就限制性.多个defer的执行顺序是固定的,谁在前面谁就先执行

最后的最后补充一点: js的执行都会阻塞html的解析过程.这个是由浏览器的性质决定的,好像是因为 js解析和执行的线程和 UI线程是互斥的关系?

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYvLGF8H' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片