Home > Article > Web Front-end > A brief analysis of defer and async attributes in script tags
1. Foreword
The code I saw written by my senior is as follows
<script src="#link("xxxx/xx/home/home.js")" type="text/javascript" async defer></script>
unexpectedly has async and defer attributes at the same time. I thought it must be some kind of black technology from my senior driver. What will happen if the two come together? A magical chemical reaction, so I quickly flipped through the books and documents with a reverent heart, and first reviewed their respective definitions.
Second, do some investigation
Let’s take a look at the definitions of async and defer first. Open the Red Book Telescope and it is introduced like this
2.1 defer
The purpose of this attribute is to indicate that the script will not Affects the structure of the page. That is, the script will be delayed until the entire page has been parsed before running. Therefore, setting the defer attribute in the <script> element is equivalent to telling the browser to download immediately but delay execution. </script>
The HTML5 specification requires that scripts be executed in the order in which they appear, so the first deferred script will be executed before the second deferred script, and these two scripts will be executed before the DOMContentLoaded event. In reality, delay scripts are not necessarily executed in sequence, nor are they necessarily executed before the DOMContentLoad time is triggered, so it is best to only include one delay script.
2.2 async
This attribute is similar to defer and is used to change the behavior of processing scripts. Also similar to defer, async only works on external script files and tells the browser to download the file immediately. But unlike defer, scripts marked async are not guaranteed to be executed in their order.
The second script file may be executed before the first script file. It is therefore important to ensure that the two are not dependent on each other. The purpose of specifying the async attribute is to prevent the page from waiting for the two scripts to be downloaded and executed, thereby loading other content of the page asynchronously.
In summary, these two attributes will cause the script tag to be loaded asynchronously, but the timing of execution is different. Quoting a picture from an answer on segmentfault
The blue line represents network reading, the red line represents execution time, both of which are for scripts; the green line represents HTML parsing.
That is to say, async is out-of-order, while defer is executed sequentially, which determines that async is more suitable for libraries such as Baidu Analytics or Google Analytics that do not rely on other scripts. From the picture, you can see that the loading and parsing of an ordinary <script> tag are synchronous, which will block the rendering of the DOM. This is why we often write <script> at the bottom of <body> First, in order to prevent long-term white screens caused by loading resources, another reason is that js may perform DOM operations, so it must be executed after all DOMs have been rendered. </script>
2.3 really?
However, this picture (almost the only answer found on Baidu) is not rigorous. This is just a standard situation, and most browsers will make optimizations when implementing it.
Let’s see how chrome does it
"WebKit Technology Insider":
1. When the user enters the web page URL, WebKit calls its resource loader to load the web page corresponding to the URL.
2. The loader relies on the network module to establish a connection, send requests and receive replies.
3. WebKit receives data from various web pages or resources, some of which may be obtained synchronously or asynchronously.
4. The web page is handed over to the HTML interpreter and converted into a series of words (Token).
5. The interpreter builds nodes (Node) based on words to form a DOM tree.
6. If the node is JavaScript code, call the JavaScript engine to interpret and execute it.
7. JavaScript code may modify the structure of the DOM tree.
8. If the node needs to rely on other resources, such as images, CSS, videos, etc., call the resource loader to load them, but they are asynchronous and will not hinder the continued creation of the current DOM tree; if it is a JavaScript resource URL ( Without marking asynchronous mode), you need to stop the creation of the current DOM tree until the JavaScript resource is loaded and executed by the JavaScript engine before continuing the creation of the DOM tree.
So, generally speaking, the Chrome browser will first request the HTML document, then call the corresponding resource loader for the various resources in it to make asynchronous network requests, and at the same time perform DOM rendering until it encounters the <script> tag At this time, the main process will stop rendering, wait for the resource to be loaded, and then call the V8 engine to parse js, and then continue DOM parsing. My understanding is that adding the async attribute is equivalent to opening a separate process to load and execute independently, and defer has the same effect as placing <script> at the bottom of <body>. </script>
3. Experiment 1
3.1 demo
In order to verify the above conclusion, let’s test it
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.css" rel="stylesheet"> <link href="http://cdn.staticfile.org/foundation/6.0.1/css/foundation.css" rel="stylesheet"> <script src="http://lib.sinaapp.com/js/angular.js/angular-1.2.19/angular.js"></script> <script src="http://libs.baidu.com/backbone/0.9.2/backbone.js"></script> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script> </head> <body> ul>li{这是第$个节点}*1000 </body> </html>
A simple demo, quoting 2 CSS and 3 JS from various CDNs, and creating 1000 li in the body. Verify using Chrome's Timeline by adjusting the location of external reference resources and adding relevant attributes.
3.2 Placed in
Asynchronously load resources, but it will block the rendering of
and a white screen will appear. Execute the script immediately in order3.3 Placed at the bottom of
Load resources asynchronously, wait until the content in
is rendered and loaded and execute JS in order3.3 Place it in the
header and use asyncload resources asynchronously, and load The JS resources will be executed immediately after completion, and will not be in order. Whoever is faster will go first
3.4 Place it in the
header and use deferto load the resources asynchronously, and then execute JS in order after the DOM is rendered.
3.5 Place it in the
head and use async and defer at the same timeThe performance is consistent with async. I opened my mind and exchanged the positions of these two attributes to see if there will be an overlay effect. The results were found to be consistent = =,
To sum up, under the webkit engine, the recommended way is still to write <script> at the bottom of <body>, if you need to use independent libraries such as Baidu Google Analytics or Bulianzi You can use the async attribute. If your <script> tag must be written in the <head> header, you can use the defer attribute. 4. Compatibility What is it, compatibility? </script>
On caniuse, async is not supported in IE
The defer attribute may be specified even if the async attribute is specified, to cause legacy Web browsers that only support defer (and not async) to fall back to the defer behavior instead of the synchronous blocking behavior that is the default. 5. ConclusionIn fact, the safest way is to write <script> at the bottom of <body>. There will be no compatibility issues, no white screen issues, and no execution order issues. Sit back and relax, and don’t make any defers. Flowers with async~</script>
Currently, we have only studied the rendering mechanism of Chrome's webkit. Firefox and IE need to be further studied. The rendering of images, CSS and other external resources needs to be studied.