Asynchronous js connection. Applying the async and defer attributes to the script tag

A way to speed up the loading of website web pages by optimizing Java script files and html code pages.

Javascript represents a certain problem for modern resources. They are simply overloaded with them and this leads to slow loading and, therefore, poor display quality.

If your network connection is slow, which is typical for mobile communications And remote users, then loading slows down significantly, causing justifiable irritation.

When the browser reads the html code, it displays the result sequentially. Those. each line follows each other, but at the place where javascript is installed, a stop occurs - those who use this language to write resources have certainly encountered this problem.

The solution to the problem is asynchronous loading. Just place the javascript at the end of the page's html code. This will lead to the fact that the loading of “java” will be delayed in time, ensuring the correct display of the page and a quick start for users of the resource. Most serious resources are switching to asynchronous loading, trying to retain their audience and introduce useful innovations.

Let's look at several ways to optimize javascript loading.

This javascript file is loaded as follows:

< script src= type= "text/javascript" >

HTML5 prudently took care of the problem of loading a page with “java”. It has the ability to set asynchronous loading of the script, which increases the speed of displaying the resource significantly. To do this, add the async or defer parameters to the html code, as indicated below:

< script async src= "//www.адрес_сайта/script.js" type= "text/javascript" >

< script defer src= "//www.адрес_сайта/script.js" type= "text/javascript" >

What is the difference between async and defer attributes?

Both options work with asynchronous javascript loading. The difference lies in the display time and the start of the script execution.

The async attribute will run the script immediately after full load, but will skip loading window.

When you set the defer attribute in the page code, javascript will queue between other scripts without disturbing their execution order. It will start working only after the page is fully loaded, but will miss the DOMContentLoaded(document object) event.

However, these attributes do not work in some modern browsers. For example, Internet Explorer does not support the async and defer attributes. And document.write lines won't help anything in script.js files

Special script from Google for asynchronous javascript loading

Google has become a leader in developing technologies that allow website pages to load in record time. In addition, the rating search engine Google demotes sites with poor performance, so pay attention to a special creak from Google webmasters designed to load javascript asynchronously.

To apply this script, simply replace

on

Then we include the script file extsrc.js

We get the following code:

< script src= "//extsrcjs.googlecode.com/svn/trunk/extsrc.js" > < script extsrc= "...." >

Unfortunately, this method also does not work for files with document.write

This method is suitable for all browsers without exception and even works with document.write

In the html code of the page we create an empty div block:

And at the end of the html code, before , we insert a script for asynchronous loading:

Here is any file or script that needs to be loaded. // move it to real position

display document.getElementById("script_block").appendChild(document.getElementById("script_ad")); // show document.getElementById("script_ad").style.display = "block"; Please note that in versions of IE lower than 6, inclusive, this download does not work. But I'm not sure that such users remain - they are a minority. Other browsers and services use this optimization for javascript what is reflected

in the best possible way

on the speed of loading resources. Note: Below is a translation of an article from Steve Souders (author of the famous Yahoo! tips regarding client performance) “Coupling async scripts”. Steve analyzes the loading behavior of JavaScript files and suggests several ways to bypass their blocking properties. My comments below are in italics. Most of my work is in Lately was devoted to asynchronous loading of external scripts. If scripts are loaded in the normal order (), then they block the loading of all other page components ( V latest versions This is not the case in Firefox and Safari, but we are talking mainly about 70% of IE users ) and block the rendering of the entire part of the page that is located below the call to the scripts in the HTML code. This can be seen on the test page, we place the scripts below for example, using dynamic creation

objects after the combined event window.onload is fired ) prevents this browser behavior, which speeds up page loading. The only problem with loading scripts asynchronously is their interaction with internal ( inline), which use variables defined in an external script. If an external script is loaded asynchronously without any knowledge of the internal code of the HTML page, then it is quite possible that ( and it will occur in most cases) when some variables will not be defined at the time of their use. Therefore, it is necessary to ensure that external scripts loaded asynchronously and internal page scripts are linked: internal scripts are not executed until asynchronous scripts will not load completely.

There are several ( standard) ways to connect asynchronously loaded scripts with other JavaScript code:

  • window onload. Execution of internal JavaScript code can be tied to the window onload event. It is very easy to use, but some of the scripts can be executed earlier.
  • onreadystatechange in the script. The internal code can be bound to the onreadystatechange and/or onload events. (It will be necessary to use both options to cover everything popular browsers.) In this case there will be more code, it will be more complex, but there will be a guarantee that it will be executed immediately after loading the corresponding external files.
  • Built-in calls. External scripts can be modified to include at the very end a call to a small piece of code that will call the corresponding function from the internal code. This is all great if external and internal scripts are developed by the same team. But in the case of using third-party developments, this will not provide all the necessary flexibility for linking external scripts with internal code.

In this article, I simultaneously (no pun intended!) cover two issues: how asynchronous scripts speed up page loading and how you can combine asynchronous scripts and internal ones using a modified version of the loader from John Resig ( by jQuery) is a double script tag template. An illustration of this is my recent work sorting UA Profiler results. I did it using the sorting script script from Stuart Langridge. In about 5 minutes I was able to add his script to a page to sort the results table. With a little more time, I was able to make this script load asynchronously and speed up page loading by over 30% using the asynchronous script docking technique.

Regular script calls

I originally added Stuart Langridge's sorting script to the UA Profiler page in the usual way(via ), this can be seen in the variant with a regular script call. The loading diagram is shown in Fig. 1.

Rice. 1. Diagram of loading scripts in the usual case.

Although sorting the data in the table worked, it did not make me happier because the page loading slowed down. In Fig. 1 clearly shows how my version of the script (named sorttable-async.js) blocks all other HTTP requests on the page (in particular, arrow-right-20x9.gif), which slows down the page loading. All loading charts were taken using Firebug 1.3 beta. In this version of Firebug, the red line displays the onload event. (And the blue line corresponds to the domcontentloaded event.) For the version with a regular script call, the onload event fires at 487 milliseconds.

The sorttable-async.js script is not necessary to initially render the page: columns can only be sorted after the columns themselves have been rendered. This situation (external scripts that are not used to initially render the page) is the number 1 candidate for implementing asynchronous loading. The asynchronous script loading option connects this script using DOM methods to create a new script tag:

var script = document.createElement("script"); script.src = "sorttable-async.js"; script.text = "sorttable.init()"; // this is explained in the next section document.getElementsByTagName("head").appendChild(script);

The HTTP loading diagram for asynchronous loading of scripts is shown in Fig. 2. It's worth noting how the asynchronous approach prevents blocking behavior: sorttable-async.js and arrow-right-20x9.gif are loaded in parallel. This reduces total time loading time is 429 ms.

Rice. 2. Diagram of loading scripts in the asynchronous case

Dual Script Template by John Resig

Allows you to speed up page loading, but in this case there is still room for improvement. By default, the sorting script calls itself by attaching sorttable.init() to the handler onload events for this script. Some performance improvements ( and code reduction) can be achieved by calling sorttable.init() inside a script tag to call it immediately after the external script is loaded ( connected via src). In this case, I'm using a single function as the "API", but I guess this case illustrates a maximally extensible pattern that allows you to use external module without any assumptions about it further use (classic situation of using JavaScript logic from external developers).

I have already described three methods for connecting internal code with asynchronous loading of external scripts: window onload, onreadystatechange for the script, and a handler built into the script. Instead, I used a technique from John Resig - the double script tag pattern. John describes how to link internal scripts with external file loading like this:

jQuery("p").addClass("pretty");

In this case, the code inside only fires after loading ( and initialization) external script has ended. This script linking approach has several obvious advantages:

  • simpler: one script tag instead of two
  • more transparent: the connection between internal and external code is more obvious
  • safer: if the external script does not load, the internal code will not be executed, which will prevent errors related to undefined variables from appearing

This is a great pattern for loading external scripts asynchronously. However, to use it we will have to make changes both to the internal code and to external file. For the internal code, I had to add the third line already mentioned above, which exposes the script.text property. To complete the docking process, you need to add sorttable-async.js to the end:

var scripts = document.getElementsByTagName("script"); var cntr = scripts.length; while (cntr) ( var curScript = scripts; if (-1 != curScript.src.indexOf("sorttable-async.js")) ( eval(curScript.innerHTML); break; ) cntr--; )

This code goes through all the scripts on the page, finds the necessary block, which should load itself (in this case, it is a script with a src containing sorttable-async.js). It then executes the code that is added to the script (in this case sorttable.init()) and thus calls itself. (Small note: although when loading the script, the text in it was added using text properties, it is accessed using the innerHTML property. This is necessary to ensure cross-browser compatibility.) Using this optimization, we can load an external script file without blocking the loading of other resources, and execute the one linked to this script internal code.

It is also worth noting that the described technique can only be a replacement for direct modification of an external script. If such a modification is not possible, we can only use the installation of a load check at an interval:

var _on_ready_execution = setInterval(function() ( if (typeof urchinTracker === function) ( urchinTracker(); clearInterval(_on_ready_execution); ) ), 10);

This approach has already been described in the book “Overclock your website”, however, it involves additional load on the processor for constant checking the required script is ready and does not work if the external file is unavailable: the check continues to run.

However, in the case of checking by interval, we do not need to modify the external file at all, but in the case of double use of the script tag, this is simply necessary. Interval checking can be improved if, after some time has passed (5-10 seconds, for example), you restart the download of the external file (changing the original script tag using a unique GET parameter), and after several unsuccessful restarts, stop downloading altogether (perhaps with some then an error message).

Lazy loading

The overall loading time can be reduced even further by using " lazy loading» script (load it dynamically as part of the onload event handler). An example of this behavior is located on the page with

HTML is designed in such a way that the web page is loaded sequentially line by line, loading in turn all the elements included in the html code. And if one of them is unavailable (for example, javaScript from an external site is not read), then further loading of the site stops.

When unreadable JS is at the top of the page, the visitor may not see anything at all.

Therefore, when you use JavaScript from third-party sites on your website, for example to display ads, it is highly advisable to load them asynchronously. In this case, third-party JS will not delay the loading and display of your site.

Unfortunately, not all advertising networks provide the ability to asynchronously load scripts. Therefore, in this article I will tell you how to change synchronous loading code to asynchronous. If the owner of this script does not provide this option.

Standard synchronous JS loading

Typically, calling a script from an external server looks like this:

Asynchronous loading script like Google/Adsense does it

I got the idea from Google/Adsense. In order for the script to load asynchronously from the rest of the HTML code, you need to add async to the call code.

And now for the code to load asynchronously, our script call from the external server should look like this:

As you can see, everything is simple. True, this will only work in browsers that support the HTML5 standard. At the time of writing this article, there are an absolute majority of such browsers.

The proposed option is not 100% universal. Many scripts simply stop working after making these changes. According to reviews on the Internet, this method does not work if the element is used in the script document.write.

Reliable asynchronous loading option

If the first suggested option does not work for you. Then take advantage the following recommendations. This is essentially lazy loading. Since the real script call occurs at the very end HTML pages, that is, when all the necessary content of the page is already on the screen.

In the place on the page where you want to display the result JavaScript work you need to create an empty div block:

Then at the end of the page before the closing BODY tag we insert a script for asynchronous loading:

// JavaScript that needs to be loaded asynchronously // move it to the actual display position document.getElementById("script_block_0").appendChild(document.getElementById("script_ad_0"));

// show document.getElementById("script_ad_0").style.display = "block";

If there are several advertising blocks, then for each of them you need to repeat everything, creating unique individual DIV blocks. Don't forget to change the class names and DIV IDs. In my example, it is enough to change the number zero, in the new block it needs to be replaced with 1, and so on. This option is more complex, but it works everywhere except for very ancient browsers such as Internet Explorer

6. Which, fortunately, is almost never found on users’ computers.


) I wrote about the effect JavaScript files have on the Critical Rendering Path (CRP).

JavaScript is a blocking resource for the parser. This means that JavaScript blocks parsing of the HTML document itself. When the parser reaches the ‹script› tag (it doesn’t matter whether it’s internal or external), it stops, picks up the file (if it’s external) and runs it.


This behavior can be problematic if we load multiple JavaScript files on a page, as it increases first render time even if the document doesn't actually depend on those files.

Luckily, the element has two attributes, async and defer, which give us control over how external files are loaded and executed.

Before understanding the difference between these two attributes, let's look at what happens in their absence. As stated earlier, by default JavaScript files interrupt parsing of the HTML document until they are received and executed.
Let's take an example where the element is located somewhere in the middle of the page:


... ... ....

This is what will happen when the parser processes the document:

HTML parsing is paused until the script is downloaded and executed, thereby increasing the amount of time until the first render.

async attribute

Async is used to indicate to the browser that the script Maybe be executed asynchronously.
The HTML parser does not need to stop when it reaches the tag to load and execute. Execution can occur after the script is received in parallel with document parsing.



The attribute is available only for files connected externally. If an external file has this attribute, then it can be loaded while the HTML document is still being parsed. The parser will pause to execute the script as soon as the script file is loaded.



defer attribute

The defer attribute tells the browser that the script should be executed after the HTML document has been completely parsed.



As with asynchronous script loading, the file can be loaded while the HTML document is being parsed. However, even if the script file is fully loaded before the parser finishes, it will not be executed until the parser finishes running.



Asynchronous, delayed or normal execution?

So, when should you use asynchronous, deferred or normal? JavaScript execution? As always, it depends on the situation and there are several questions that can help you make the right decision.

Where is the element located?

Asynchronous and lazy execution are most important when the element is not at the very end of the document. HTML documents are parsed in order, from opening to closing. If the external JavaScript file is placed immediately before the closing tag, then using async and defer becomes less appropriate, since the parser will have parsed most of the document by then and the JavaScript files will no longer have an effect on it.

Is the script self-sufficient?

For files that do not depend on other files and/or do not have any dependencies, the async attribute will be most useful. Since we don't care when the file is executed, asynchronous loading is the most suitable option.

Does the script rely on a fully parsed DOM?

In many cases, the script file contains functions that interact with the DOM. Or perhaps there is a dependency on another file on the page. In such cases, the DOM must be completely parsed before the script is executed. Typically, such a file is placed at the bottom of the page to ensure that everything has been parsed for it to work. However, in a situation where for some reason the file must be located in a different location, the defer attribute can be useful.

The script is small and dependent?

Finally, if the script is relatively small and/or depends on other files, then it may be worth defining it inline. Although the inline code blocks parsing of the HTML document, it should not be much of a problem if the document is small in size. Also, if it depends on other files, some minor locking may be needed.

Support and modern browser engines

Support for the async and defer attributes is very common:




It's worth noting that the behavior of these attributes may vary slightly between JavaScript engines. For example, in V8 (used in Chromium), an attempt is made to parse all scripts, regardless of their attributes, into a separate dedicated thread for script execution. Thus, the "parser-blocking" nature of JavaScript files should be minimized by default.

The reason for writing this post was that more than once I had to notice that inserting button code into a page various services(for example: VKontakte, Facebook, Twitter, Odnoklassniki) led to a noticeable slowdown in page loading and display. This is the case when the connection of external javascript of these social services is used.
If we use simple static graphic buttons, there are no problems, because... This is a minimum of graphics and scripts that are located locally (you can see an example of the implementation here http://pervushin.com/social-button-for-blog.html). But we only see social icons. services, there are no statistics (how many people “liked” our page). Those. if we want to see statistics, we will have to connect external scripts. And here it’s worth keeping in mind that how many of these buttons we have connected, the browser is forced to download so many external scripts, i.e. This additional connections to external servers.

To show what happens if there are scripts in the section on the page, I suggest looking at a number of test examples. I will use FireFox 3.6 and FireBug.

So:
1) The simplest page with one style file, two scripts and three images:













And here is the loading diagram for it:

Please note that all images are loaded only after the longest javascript file has been loaded.
I deliberately made the loading of dummy_css.css and dummy_js.js quite long. It's just two files:

dummy_css.php

html,body(
margin:0;
padding:0;
}
.img_container(
margin:0 auto;width:500px;
}

dummy_js.php


var param=1;

So, you can see that the js file blocks the loading of all other graphics.

2) Everything is almost the same, but dummy_ js. js is loaded from an external host:

The situation is similar to the previous one:

3) Let's try to swap the css and js files in the head section (css now comes after js):







Let's look at the loading diagram:

Js still blocks images from loading, no matter what host it is loaded from.

4) Let’s increase the css loading time to 4 seconds (html code as in the case of N3):

5) Another interesting case: css is located before js, but css takes longer to load















Here the css is already blocking the loading of images...

6) Move one js inside< body>
















It can be seen that dummy_ js. js blocks loading only the third image, which is located in the html code after it. But if the css takes longer to load, then it will block the loading of graphics. It’s not hard to imagine that plugging in external scripts can greatly slow down page loading and rendering, especially if remote server

For some reason it takes a long time to respond.

Placing external scripts before

What immediately suggests itself... All scripts whose loading is not critical for the page should be placed right before the closing tag. But this can be done for those scripts in which all the logic is internal and there are no external calls from html code. If, from the html code, some functions are called from scripts that have not yet been loaded, then such situations must be handled in a special way, because you need to track the download.




But there is another problem, let me explain with an example:
$("img").click(function() (
});
});






alert($(this).attr("src"));

If the js before it takes a long time to load, then clicking on the pictures until this script is fully loaded will not lead to anything, because $(document).ready() will only work after js is fully loaded. So if there is some logic on the pages that involves event processing, then this method is not suitable.

So what is needed is a way to load scripts non-blockingly...



Create async.js:


script.src = "dummy_js.js";











and connect it:
But there is another problem, let me explain with an example:
$("img").click(function() (
});
});






$(document).ready(function() (

But if it is still more convenient to place async.js in the code, then you should slightly change the contents of async.js:

$(document).ready(function() (
var script = document.createElement("script");
script.src = "dummy_js.js";
document.getElementsByTagName("head") .appendChild(script);
}
);

So, the issue of asynchronous loading has been resolved, but the issue of synchronizing the loading of external scripts and the code that uses them remains open. Let's consider it in practice, connecting social network buttons.