Asynchronous scripts execute after deferred - javascript

Ideally, I would like to have all scripts load asynchronously but in the HTML order. As far as I know, async doesn't work that way.
With the following attributes, the general.min.js and funcs.min.js scripts do not run since there is some jQuery within them.
<script type="text/javascript" src="scripts/jquery-3.2.1.min.js" async="async"></script>
<script type="text/javascript" src="scripts/general.min.js" defer="defer"></script>
<script type="text/javascript" src="scripts/funcs.min.js" defer="defer"></script>
I understand that the attribute only affects the download and not the execution of the scripts.
How, then, is it possible to maintain the execution order after download?

If you want your scripts to download asynchronously yet execute in order, use defer. Rendering won't be blocked because script execution won't start until the document is parsed.
<script type="text/javascript" src="scripts/jquery-3.2.1.min.js" defer></script>
<script type="text/javascript" src="scripts/general.min.js" defer></script>
<script type="text/javascript" src="scripts/funcs.min.js" defer></script>
On the other hand, async will also download scripts asynchronously, but each script it executed immediately after downloading finishes. So if you include a small script below a large script, the small script will download quicker and be executed first.
EDIT #2 (EDIT #1 was replaced by #2 for clarity)
The using defer scripts makes a lot of sense when all scripts are loaded in the <head> from external sources. The download is async, DOM parsing isn't blocked, and the execution order is maintained. Yay! I will call these the deferred scripts.
However, inline scripts (<script>...</script>) in the <body> are downloaded and executed synchronously during DOM parsing, so they won't have access to the deferred scripts (because those don't execute until after DOM parsing).
What if the inline scripts need access library scripts, such as jQuery or lodash? Obviously, those library scripts must be fully downloaded and executed before the inline scripts run. This is done by putting the library scripts in the <head> without the use of defer. This makes them execute immediately, in order. Note that the deferred scripts can also access the library scripts because they are loaded even after the the inline scripts!
Phew! Here's an example:
<head>
<!-- Executed last: defered scripts -->
<script src="scripts/general.min.js" defer></script>
<script src="scripts/funcs.min.js" defer></script>
<!-- Executed first: library scripts, such as jquery, lodash, etc.. -->
<script src="scripts/jquery.min.js"></script>
</head>
<body>
<!-- Executed second: inline scripts -->
<script>
$.ready(console.log);
</script>
</body>
Just one addition: defered scripts don't need onload listeners (or $.ready, etc..) because the DOM is already loaded!

Related

Javascript inline scripts and defer scripts order of execution

I have a question about order of execution for mixed script type.
Here is my code :
<script>
if(document.documentMode) {
const firstScriptInDOM = document.getElementsByTagName('script')[0];
const polyfill = document.createElement('script');
polyfill.src = "/static/js/polyfills/polyfills.js";
firstScriptInDOM.parentNode.insertBefore(polyfill, firstScriptInDOM);
}
</script>
<script src="static/js/lib1.js" defer></script>
<script src="static/js/lib2.js" defer></script>
<script src="static/js/lib3.js" defer></script>
<script src="static/js/myOwnScriptFile.js" defer></script>
The first script tag's purpose is to load polyfills for IE if the browser is IE.
Then it should load this other scripts and execute my code.
My question is : Will the polyfills script block execution of the defered scripts ?
Thanks a lot for you time !
scripts load synchronously unless specified to load asynchronously using the async attribute, what the defer attribute does is that it loads the script only after DOM is loaded. If you append a script dynamically, it will load asynchronously.
In you scenario,
This should be the chain of execution:
check polyfill script executes
lib1
lib2
lib3
myOwnScriptFile
polyfills (downloaded only after the parser has finished execution)
To ensure all your scripts load, in the order you want them too, you could dynamically load all scripts with something like :
check if the browser is IE:
IF IE
load polyfills, and load other scripts in the onload event of the polyfills script.
ELSE
load all other scripts
The script which is not deferred will run when it is parsed.
The other scripts will be executed after the document has been parsed in the order in which they appear in the document, before DOMContentLoaded is fired.
Update
Kaiido is correct and I was a wrong in my conclusion.
The part of your script that adds the polyfill will run first but the script it loads will run asynchronously, as soon as it downloads.
You can see more details here.
You can try using document.write instead.

Do browsers execute loaded scripts in a single thread?

I can write the following:
<script src="file1.js" defer></script>
<script src="file2.js" defer></script>
<script src="file3.js" defer></script>
Which means files can be downloaded in parallel but executed only one after another. However, I can add async attribute to allow browser execute code in a random order.
<script src="file1.js" async></script>
<script src="file2.js" async></script>
<script src="file3.js" async></script>
If I'm interested in the performance boost, can the second block be executed faster? As I see it if a browser executes all JavaScript in one thread, then the total execution time for 3 scripts will be no different from the first block, only the order of execution may be different. Am I right?
If I'm interested in the performance boost, can the second block be executed faster?
Since the scripts will be downloaded and executed asynchronously, yes, it will reduce the page loading time.
Author of the Script-injected "async scripts" considered harmful compares three methods of including a script: an injected script, a blocking script and a script with async attribute. The result is:
script execution onload
----------------- ------------------ --------
script-injected ~3.7s ~3.7s
blocking script ~2.7s ~2.7s
async attribute ~1.7s ~2.7s
As you see, the async attribute gives the best performance. If scripts execution order doesn't matter, you should definitely use it.
As for your title question:
Do browsers execute loaded scripts in a single thread?
Yes, because JavaScript is single-threaded, but it doesn't matter in terms of performance. Downloading a script takes much longer time than actually executing it, so you should focus on optimizing the download part.
I've made a test. I created a simple script:
for (var i = 0; i < 1e8; i++);
I put the same script into two files, test1.js and test2.js. Then I made two HTML files, the first without async and the second with:
test1.html:
<!DOCTYPE html>
<script src="test1.js"></script>
<script src="test2.js"></script>
test2.html:
<!DOCTYPE html>
<script src="test1.js" async></script>
<script src="test2.js" async></script>
Then I opened these HTML files in my browser and checked Timeline tab in Chrome DevTools:
test1.html:
test2.html:
As you see, in both cases scripts are not executed asynchronously.
See also: Is JavaScript guaranteed to be single-threaded?.

Asynchronously load to eliminate render-blocking js & css?

I've just spent a really long time googling... and only find half answers everywhere. I am using the google page speed insights to improve my website and it tells me to asynchronously load my javascript. I found a couple of codes, but they didn't explain how to load MORE than one js file AND how to load the css as well. I also couldn't find anywhere where it tells me in what order to load it. Can anyone help?
NOTE: I DID try to move the js to the footer, but then my mobile menu no longer works (which uses the expand.js file)
The Javascript files I need to asynchronously load are:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="js/h5.js"></script>
<script language="javascript" type="text/javascript" src="js/expand.js"></script>
My CSS:
<link rel="stylesheet" type="text/css" href="style.css">
Asynchronous loading of scripts can get pretty complicated. Have you tried using the async attribute? E.g.:
<script async src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script async language="javascript" type="text/javascript" src="js/h5.js"></script>
<script async language="javascript" type="text/javascript" src="js/expand.js"></script>
However, this can cause undesired race conditions. All this means is the rest of your site is not going to wait for these three JavaScript files to load before loading other resources. This may or may not improve the speed of your site depending on what is contained, and can produce some wonky results in terms of dependency management which you'll have to account for in your scripts.
It is usually recommended by the big G to create two small files: a CSS and a JavaScript file to be included in your <head/> which contains all the styles and logic for the above-the-fold content (or better yet: inline them, even though this increases the DOM size). Source.
How To Asynchronously load Javascript File?
Well to load javascript file you just need to include "async" attribute in <script> tag i.e.
<script src="your path" async></script>
Now this script will be downloaded in background while it will not cause JS rendering issue.
Note: If you're using some jquery plugin i.e. image zoom or any thing else, it will take an extra time to load but after that it will work properly.
Why to use "DEFER" keyword with "ASYNC" ?
You can also use defer attribute along with async attribute. <script> tag with async attribute forces file to be downloaded in background and execute as soon it is downloaded. but async with defer attribute forces <script> tag to execute once entire site is loaded.
<script src="" async defer></script>
How To Asynchronously load CSS file?
If you want to load your CSS file asynchronously first you have to place provided <script> in your head file and then you can use loadCSS() function to load you CSS file asynchronously.
<script>
// https://github.com/filamentgroup/loadCSS
!function(e){"use strict"
var n=function(n,t,o){function i(e){return f.body?e():void setTimeout(function(){i(e)})}var d,r,a,l,f=e.document,s=f.createElement("link"),u=o||"all"
return t?d=t:(r=(f.body||f.getElementsByTagName("head")[0]).childNodes,d=r[r.length-1]),a=f.styleSheets,s.rel="stylesheet",s.href=n,s.media="only x",i(function(){d.parentNode.insertBefore(s,t?d:d.nextSibling)}),l=function(e){for(var n=s.href,t=a.length;t--;)if(a[t].href===n)return e()
setTimeout(function(){l(e)})},s.addEventListener&&s.addEventListener("load",function(){this.media=u}),s.onloadcssdefined=l,l(function(){s.media!==u&&(s.media=u)}),s}
"undefined"!=typeof exports?exports.loadCSS=n:e.loadCSS=n}("undefined"!=typeof global?global:this)
</script>
Now you just have to use loadCSS function.
<script>
loadCSS("https://www.yourCSSLinkHere.com");
</script>
In this way you can load your CSS and JS file Asynchronously.
You can use HTTP/2.0 to overcome speed issue because HTTP/2.0 allows your file to be downloaded parallel but HTTP/1.0 won't allow your file to be download parallel, in other way HTTP/1.0 follows FIFO(First In First Out) rule.

Any way to control Javascript Async Load Order?

How do I set the load and execution order of two external async Javascript files?
Given the following...
<script src="framework.js" async></script> // Larger file
<script src="scripts.js" async></script> // Small file
Though second in order scripts.js is downloading and executing before framework.js due to it's file size, but scripts.js is dependent on framework.js.
Is there a way natively to specify the load and execution order whilst still maintaining async properties?
You want to use defer if you want to preserve the execution order. What defer does is it async downloads the script, but defers execution till html parsing is done.
<script src="framework.js" defer></script>
<script src="scripts.js" defer></script>
However, you may want to start creating custom bundles once the number of scripts go higher.
You can see the difference here
If you need a solution for IE9 (since it does not support the defer attribute), I have created a small loader script:
https://github.com/mudroljub/js-async-loader
It loads all your scripts asynchronously and then executes them in order.

javascripts aren't executed sequentially in modern browser?

I know that in general, the scripts are loaded and executed in the order. But I have a problem that they are not executed in the right order, sometime the second script is executed and then the first script.
I am using jsp and tile. In the jsp template, I only load the common js files and then load specific js files for specific tile
It seems like browser loads and executes the scripts asynchronously
How to force it to load and execute scripts sequentially?
I've searched a lot of places for the answer but not work.
Template:
<html>
<head>
<title></title>
//load common js files
</head>
<body>
<tiles:insert attribute="header"/>
<tiles:insert attribute="content"/>
<tiles:insert attribute="footer"/>
</body>
</html>
Specific tile:
<div>
Test
</div>
//load js files
<script type="text/javascript" src="1.js"></script>
<script type="text/javascript" src="2.js"></script>
<script type="text/javascript" src="3.js"></script>
Edit:
These javascript files are very simple, they just print out number 1,2 and 3
But sometime It prints the wrong order.
The "content" div will be replaced by the tile and I don't know whether it is a type of loading resource dynamically: Dynamically loading JavaScript synchronously
There is a great article on html5rocks about script loading.
http://www.html5rocks.com/en/tutorials/speed/script-loading/
the simplest way would be to use defer but it's not supported by IED<10 and opera mini.
<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>
Spec says: Download together, execute in order just before
DOMContentLoaded. Ignore “defer” on scripts without “src”.
IE < 10 says: I might execute 2.js halfway through the execution of 1.js. Isn’t that fun??
The ultimate solution but less supported would a combination of dynamic inclusion and forcing async attribute to false.
[
'1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
it requires a work around using onreadystatechange on IE<10

Categories