Five minutes to understand async and defer
Script tag
When we want to insert a script into a web page, the standard way is to use the script tag(i.e. <script>). The first impression that comes to people's mind about script tag is - BLOCKING .
The book High Performance Web Sites Rule 6 suggests to put scripts at the bottom of html body. The article will examine how putting scripts at varying positions affects performance and how async and defer attributes work for script tag.
First thing first, when a script is referenced in a html page, the browser does two things for you:
- Retrieve/Load the script content, this is NON-BLOCKING!
- Run the script content, this is BLOCKING !
Assume we have two scripts on the page:
//script1.js
let t1 = +new Date;
console.log('script1 is running at', t1);
console.log('script1 element', document.getElementById('load-experiment'));
while((+new Date) - t1 < 1000) {
// delay 1 seconds
}
console.log('script1 finishes running at', +new Date);
//script2.js
let t2 = +new Date;
console.log('script2 is running at', t2);
console.log('script2 element', document.getElementById('load-experiment'));
while((+new Date) - t2 < 2000) {
// delay 2 seconds
}
console.log('script2 finishes running at', +new Date);
Put script tags in head
<!--all-in-head.html-->
<html>
<head>
<title> test js tag async and defer attributes</title>
<script src='./script1.js'></script>
<script src='./script2.js'></script>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
</body>
</html>
The console output:
script1 is running at 1496747869008
script1.js:4 script1 element null
script1.js:8 script1 finishes running at 1496747870008
script2.js:2 script2 is running at 1496747870009
script2.js:4 script2 element null
script2.js:8 script2 finishes running at 1496747872009
Conclusion:
- When we open the html in the browser, we can notice the delay of page load. The page goes blank before it renders correctly. This is due to the fact that the running of the two scripts blocks the DOM rendering.
- When scripts are running, they are not able to fetch the DOM element (i.e. document.getElementById('load-experiment') being null). This is because scripts are run before DOM is rendered.
- Scripts themselves are blocking each other. They are run in the order specified in the html. Script2 is run after script1 finishes running.
Put script tags at the bottom of body
This is the suggestion from the Rule 6 of the book High Performance Web Sites.
<!--all-in-body.html-->
<html>
<head>
<title> test js tag async and defer attributes</title>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
<script src='./script1.js'></script>
<script src='./script2.js'></script>
</body>
</html>
The console output:
script1 is running at 1496751597679
script1.js:4 script1 element <h1 id="load-experiment"> hello world </h1>
script1.js:8 script1 finishes running at 1496751598679
script2.js:2 script2 is running at 1496751598680
script2.js:4 script2 element <h1 id="load-experiment"> hello world </h1>
script2.js:8 script2 finishes running at 1496751600680
Conclusion:
- No page delay and blank page when opening the page in browser, as scripts are put/run after the DOM is ready.
- Scripts can correctly fetch DOM elements.
- Scripts themselves are blocking each other, as the same as the first example.
However, puting all scripts at the bottom of body sometimes doesn't fit in some specific real life cases. For example, if we aim to calculate the ATF[https://en.wikipedia.org/wiki...] of a page, we can't simple wait for the DOM to render. We have to load and run some scripts beforehand and then fire the ATF marker when the ATF is ready, which is definitely before DOM is ready for web pages with scrolling content. Therefore, it seems reasonable to come out with the next solution.
Put scripts seperately in head and body based on requirements.
Put scripts that needs pre-running at head and others at the bottom of body. E.g.
<html>
<head>
<script src="headScripts.js"></scripts>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
<script src="bodyScripts.js"></script>
</body>
</html>
defer!
The main disadvantage of putting scripts at the bottom of body is that the scripts will only be retrieved/loaded after the DOM is rendered. As we said, retrieving/loading the script content is NON-BLOCKING while running the script content is the BLOCKING part. We can improve the web performance if we can retrieve/load the scripts while the DOM is rendering, rather than wait for the DOM to complete rendering. This works pretty well especially when the scripts are large. This is why defer is introduced. defer loads the scripts simultaneously but only runs the scripts after the DOM is ready.
<!--defer.html-->
<html>
<head>
<title> test js tag async and defer attributes</title>
<script defer src='./script1.js'></script>
<script defer src='./script2.js'></script>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
</body>
</html>
The console output:
script1 is running at 1496760312686
script1.js:4 script1 element <h1 id="load-experiment"> hello world </h1>
script1.js:8 script1 finishes running at 1496760313686
script2.js:2 script2 is running at 1496760313686
script2.js:4 script2 element <h1 id="load-experiment"> hello world </h1>
script2.js:8 script2 finishes running at 1496760315686
Conclusion:
- The result is the same as putting scripts at the bottom of body. We can conclude from the result that the scripts are run after the DOM is ready as we can indeed fetch the DOM elements.
- Even the defered scripts follow the order rule specified in html, script1 is run after script2.
async!
Web pages often contain some add-on features which are strictly independently and NOT must-to-have, such as the comment and chat functionalities on some pages. As the features are independent, they don't have the running order restriction. In this case, we can use async
<!--async.html-->
<html>
<head>
<title> test js tag async and defer attributes</title>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
<script async src='./script1.js'></script>
<script async src='./script2.js'></script>
</body>
</html>
We can observe different console outputs when we refresh the page:
- The running order of script1 and script2 varies
- The result of fetching DOM element is inconsistent
As async scripts don't guarantee the running order, this is often the source of potential hidden bugs. Have a second thought before using async and mare sure these scripts are strictly independent.
Conclusion
The general rule to import script is:
<html>
<head>
<!--headScripts.js is the script that has to be loaded and run before DOM is ready-->
<script src="headScripts.js"></scripts>
<!--bodyScripts.js loads first and runs after DOM is ready-->
<script defer src="bodyScripts.js"></script>
</head>
<body>
<!--body content-->
<h1 id='load-experiment'> hello world </h1>
<!--independent scripts,nice-to-have -->
<script async src="forumWidget.js"></script>
<script async src="chatWidget.js"></script>
</body>
</html>
Reference
Notice
- If you benefit from this Repo,Please「Star 」to Support.
- If you want to follow the latest news/articles for the series of reading notes, Please 「Watch」to Subscribe.
Five minutes to understand async and defer的更多相关文章
- script标签不带属性与带async、defer的区别
<script> 当页面解析到script标签时,会停止解析并下载对应的脚本,并马上执行,执行完毕后再继续解析页面 <script async> async 在下载脚本的同时不 ...
- js之script属性async与defer
概念 默认情况下js的脚本执行是同步和阻塞的,但是 <script> 标签有 defer 和 async 属性, 这可以改变脚本的执行方式,这些都是布尔类型了,没有值,只需要出现在 < ...
- 转:script中的async和defer
script中的async和defer defer: This Boolean attribute is set to indicate to a browser that the script is ...
- 浅谈script标签中的async和defer
script标签用于加载脚本与执行脚本,在前端开发中可以说是非常重要的标签了.直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载&执行的过程中,会阻塞后续的DOM渲染 ...
- async与defer
<script>元素的几种常见属性: async 异步加载,立即下载,不应妨碍页面其他操作,标记为 async 的异步脚本并不保证按照指定的先后顺序执行,因此异步脚本不应该在加载期间修改 ...
- JavaScript异步加载的三种方式——async和defer、动态创建script
一.script标签的位置 传统的做法是:所有script元素都放在head元素中,必须等到全部js代码都被下载.解析.执行完毕后,才能开始呈现网页的内容(浏览器在遇到<body>标签时才 ...
- JS—ajax及async和defer的区别
###1.ajax “Asynchronous Javascript And XML”(异步 JavaScript 和 XML) 使用: 如不考虑旧版本浏览器兼容性, // 第一步创建xhr对象 v ...
- script 标签里的 async 和 defer
无 async 和 defer 浏览器立即加载并执行指定脚本(读到即加载并执行),阻塞文档解析 async 脚本的加载执行和文档的加载渲染 并行. defer 脚本的加载和文档的加载渲染并行,但脚本的 ...
- script async和defer
1.没有async和defer,html解析时遇到script标签,会先去下载js文件,文件加载完成立即执行,执行完了再开始解析后面的html,是一个顺序流的过程 2.async,加载和渲染后续文档元 ...
随机推荐
- Nmap扫描二级目录
nmap --script http-enum -p80 192.168.2.100 //namp扫描2级目录
- netcore程序部署 ubuntu 16.0.4 报错 The type initializer for 'System.Net.Http.CurlHandler'的解决方案
最近业务扩展需要把netcore程序部署到ubuntu 16.0.4上,因为代码里面用到了HttpClient 请求. 部署ubuntu后一直报错 参考地址:https://github.com/do ...
- git 添加本地项目到远程仓库 记录一下命令
1.初始化 git init 2.关联远程仓库 git remote add origin 你的仓库地址 3.加入到本地仓库 git add * 4.推送(强推).如果不想强推 ,可以先执行下 git ...
- H.Holy Grail ( floyd )(The Preliminary Contest for ICPC Asia Nanjing 2019)
题意: 给出一个有向图,再给出6条原来不存在的路径,让你在这6条路径上添加一个最小的数,使图不存在负环. 思路: 直接6遍 floyd 输出就行了. #include <bits/stdc++. ...
- Python 条件判断 和循环
使用条件判断 if else # 条件派单 if else print('条件派单 if else') # s = input('请输入生日年号:') # birth = int(s) birth = ...
- python-redis缓存-pool
#连接池 import redis pool=redis.ConnectionPool(host='192.168.71.140', port=6379) r = redis.Redis(connec ...
- 配置Mysql远程连接
一.赋予某个用户权限 1.赋予权限格式:grant 权限 on 数据库对象 to 用户@IP(或者相应正则) 注:可以赋予select,delete,update,insert,index等权限精确到 ...
- Abp添加新的接口(扩展底层接口)
在https://aspnetboilerplate.com/Templates 创建项目之后,下载用Vs2019打开(vs2017不支持netcore3.0)结构如下: 一. 2. 在xx.core ...
- JS基础_JS的HelloWorld
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- 移动端 app
上传到蒲公英