Scripts async, defer
On this page
Scripts: async, defer
The scripts are heavier in modern browsers than in HTML: the download size is larger, and it takes more time to process.
Once the browser loads HTML meets a <script>…</script> tag, it won’t be able to continue setting up the DOM. The script should be executed by it right now. The same scenario works for the external scripts. In other words, the browser must hold on until the script loads, execute it and process the rest of the page only after.
It leads to the following two significant problems:
The scripts are not capable of seeing the DOM elements below them. Hence, they can’t add handlers. In case there exists a massive script at the the page top, “the page will be blocked”. The page content won’t be seen by the users until it downloads and runs, like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<p> Before script...</p>
<script src="https://www.w3cdoc.com/js/long.js?speed=1"></script>
<!-- This is not visible until the script loads. -->
<p> After script</p>
</body>
</html>
It is also possible to place a script at the bottom of the page. In that case, it will see the elements above and the page content won’t be blocked by it, like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
All content should be placed above the script!
(所有内容都应放在脚本上方!)
<script src="https://www.w3cdoc.com/js/long.js?speed=1"></script>
</body>
</html>
However, this is not a perfect solution. For instance, if the script is seen by the browser, only after the entire HTML document is downloaded. Note that for large HTML documents, it can be a noticeable delay. Things like this can be invisible to people who use faster connections. But, consider that many people still have a slow internet connection. Fortunately, there exist two <script> attributes that can solve the possible issues. These attributes are called defer and async.
Defer
Defer (次选)
Defer informs the browser that it should continue working with the page, load the script “in the background” and then run the script when it loads. Let’s demonstrate the example above, using the defer attribute:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<p> Before script</p>
<script src="https://www.w3cdoc.com/js/long.js?speed=1"></script>
<!-- Seen right away -->
<p> After script </p>
</body>
</html>
So, the scripts with the defer attribute don’t block the page. They always runonce the DOM is arranged, but prior to the DOMContentLoaded event, like in the example below:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<p> Before scripts</p>
<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!")); // (2)
</script>
<script src="https://www.w3cdoc.com/js/long.js?speed=1"></script>
<p> After scripts .</p>
</body>
</html>
DOMContentLoaded
(2)
The scripts that are known as deferred, on their turn, maintain their order. So, in the event of having a large script initially, then a lesser one, then the latter script waits, like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<script src="https://www.w3cdoc.com/js/long.js"></script>
<p>loaded long script </p>
<script src="https://www.w3cdoc.com/js/small.js"></script>
<p>loaded small script </p>
</body>
</html>
The browsers scan the scripts page, downloading them in parallel. It is done for improving performance. As you can notice, the example above shows the process of downloading both scripts in parallel. First, it is made by the small.js. But as required by the specification, the scripts should execute in the document order. So, the small.js waits for the long.js to execute. (浏览器扫描脚本页面,并行下载。这样做是为了提高性能。如您所见,上面的示例显示了并行下载两个脚本的过程。首先,它是由small.js创建的。但根据规范要求,脚本应按文档顺序执行。因此, small.js等待long.js执行。)
An important thing to note: Defer is only for external attributes. It is ignored when the <script> tag doesn’t have src.
Async
Async (异步)
Async signifies that the script is completely independent:
The async scripts are not waited for by the page: the content is processed and presented. DOMContentLoaded and the async scripts don’t have to wait for one another. ( the DOMContentLoaded event might occur before and after an async script, in case the latter finishes loading after the page is complete. Either it can happen after an async script, in case the latter is short or was located in the HTML-cache). Other scripts never wait for async scripts and vice versa.
So, in case of having multiple scripts, they might execute in any sequence. The one that loads first-runs first, like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<p> Before scripts </p>
<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>
<script src="https://www.w3cdoc.com/js/long.js"></script>
<script src="https://www.w3cdoc.com/js/small.js"></script>
<p> After scripts </p>
</body>
</html>
So, in the above-demonstrated example, the page content appears immediately. The DOMContentLoaded event might occur both before and after async: there are no guarantees. Async scripts don’t wait for one another. And, finally, a small.js goes second but loads before long.js. So, it runs first. That scenario is known as a “load-first” order.
Dynamic Scripts
Dynamic Scripts (动态脚本)
A script can be added dynamically with JavaScript, as follows:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<script>
let script = document.createElement('script');
script.src = "https://www.w3cdoc.com/js/long.js";
document.body.append(script); // (*)
</script>
</body>
</html>
The script begins to load as soon as it’s appended to the document (). (脚本追加到文档()后立即开始加载。)
Dynamic scripts act like “async” by default. (>默认情况下,动态脚本的作用类似于“异步”。)
In other words, they don’t wait for anything, and vice versa. (换句话说,他们不等待任何东西,反之亦然。)
The script, which loads first-runs first, as well. (首先加载首次运行的脚本。)
Take a look at the example below:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<script>
let script = document.createElement('script');
script.src = "https://www.w3cdoc.com/js/long.js";
script.async = false;
document.body.append(script);
</script>
</body>
</html>
For instance, here two scripts are added. But, without script.async=false these scripts would execute in the load-first order. With that flag, the sequence is as in the document, like here:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<script>
function loadScript(src) {
let script = document.createElement('script');
script.src = src;
script.async = false;
document.body.append(script);
}
// long.js runs first because of async=false
(//long.js首先运行,因为async = false)
loadScript("https://www.w3cdoc.com/js/long.js");
loadScript("https://www.w3cdoc.com/js/small.js");
</script>
</body>
</html>
Summary
Summary (概要)
The async and defer attributes are largely used for escaping the most common issues that users can come across. They have one common thing: downloading scripts like that never blocks the page rendering. So, it allows the user to read the page content and get acquainted with it at once. Along with the significant similarities, async and defer have essential differences. For example, async follows the load-first order, while defer keeps the document order.