asynchronous javascript loading/executing - javascript

In this post, asynchronous .js file loading syntax, someone said, "If the async attribute is present, then the script will be executed asynchronously, as soon as it is available."
(function() {
var d=document,
h=d.getElementsByTagName('head')[0],
s=d.createElement('script');
s.type='text/javascript';
s.async=true;
s.src='/js/myfile.js';
h.appendChild(s);
}()); /* note ending parenthesis and curly brace */
My question is, what does "the script will be executed asynchronously" mean? Will this script be executed in a different thread from other javascripts in the page? If yes, should we worry about synchronization issue in the two threads?
Thanks.

Usually, when you add an external script to an HTML document, the script will need to be downloaded and executed before anything else can be done on the page. In other words, the script blocks. This can take a lot of time if there are several scripts to download.
But when you load a script asynchronously, it doesn't block. The rest of the page can be loaded and other scripts can be executed while the async script is downloading. This makes pages load faster, but this also means that you can't be sure when the async script will be executed. So you can't just start using functions and objects from the async script. You have to wait and check for the async script to load.
Example:
script1.js
var foo = "bar";
script2.js
alert(foo);
doc1.html
<script type="text/javascript" src="script1.js"></script>
<script type="text/javascript" src="script2.js"></script>
Result: "bar"
doc2.html
<script type="text/javascript" src="script1.js" async="true"></script>
<script type="text/javascript" src="script2.js"></script>
Result: "bar" or undefined, depending on whether or not script1.js has loaded yet.
No threads to worry about, though. One script executes after the other, but never both at the same time. It's just the order of execution that becomes unpredictable.

It will not be executed on different thread. You do not have to worry about thread synchronization in this case.
At some point later, after your current call stack unwinds, the download of myfile.js will complete. The browser and js framework will execute your script at some point after that.

Downloaded asynchronously, not executed asynchronously. Also, it's not necessarily executed when the download finishes.

(function(doc, script) {
var js,
fjs = doc.getElementsByTagName(script)[0],
add = function(url, id) {
if (doc.getElementById(id)) {return;}
js = doc.createElement(script);
js.src = url;
id && (js.id = id);
fjs.parentNode.insertBefore(js, fjs);
};
// Google Analytics
add(('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js', 'ga');
// Google+ button
add('https://apis.google.com/js/plusone.js');
// Facebook SDK
add('//connect.facebook.net/en_US/all.js', 'facebook-jssdk');
// Twitter SDK
add('//platform.twitter.com/widgets.js', 'twitter-wjs');
}(document, 'script'));
source: https://css-tricks.com/thinking-async/

Related

JavaScript async & defer: run a script asynchronously

As far as I am aware, in the script element, the async attribute allows the script to download asynchronously (similar to images), while the defer causes the script to wait until the end before executing.
Suppose I have two scripts to include:
<script src="library.js"></script>
<script src="process.js"></script>
I would want them both to proceed asynchronously, and I would want process.js to wait until the end to start processing.
Is there a way to get the library.js script to run asynchronously?
Note
I see there appears to be some discrepancy between different online resources as to the actual role of the async attribute.
MDN & WhatWG suggest that it means that the script should execute asynchronously. Other online resources suggest that it should load asynchronously, but still blocks rendering when it is executed.
I found Sequential script loading in JavaScript which might help you:
(function(){
//three JS files that need to be loaded one after the other
var libs = [
'https://code.jquery.com/jquery-3.1.1.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/underscore.string/3.3.4/underscore.string.js'
];
var injectLibFromStack = function(){
if(libs.length > 0){
//grab the next item on the stack
var nextLib = libs.shift();
var headTag = document.getElementsByTagName('head')[0];
//create a script tag with this library
var scriptTag = document.createElement('script');
scriptTag.src = nextLib;
//when successful, inject the next script
scriptTag.onload = function(e){
console.log("---> loaded: " + e.target.src);
injectLibFromStack();
};
//append the script tag to the <head></head>
headTag.appendChild(scriptTag);
console.log("injecting: " + nextLib);
}
else return;
}
//start script injection
injectLibFromStack();
})();
Both defer and async affect when a script is executed, not when a script is downloaded. I think the confusion comes from the fact that some documentation is a bit sloppy with terms, and the term loaded is unclear as to whether it refers to the fetching of the resource, or the execution of it.
To get library.js to run asyncronously without waiting for the document to load, use async attribute, and to get process.js to wait until document has been parsed, use defer:
<script src="library.js" async></script>
<script src="process.js" defer></script>
Note that library.js is not guaranteed to run before process.js, although it probably will.

How to load 2 Javascript files async and run one after another?

I have such structure:
<script src="/Content/Scripts/1.js"></script>
<script async src="/Content/Scripts/2.js"></script>
I need to load both files async and run 2.js file after 1.js. How could I do it?
If I add async to 2.js they will run at random.
You could dynamically add your scripts, in that way they are loaded asynchronously by default.
To ensure an ordered execution you could mark them explicitly as not async.
Here is a minimum example:
<html>
<head>
<script>
[
'https://code.jquery.com/jquery-1.11.3.min.js',
'1.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
</script>
</head>
<body>
</body>
</html>
The File 1.js contains jquery code:
$("body").append("<div>It works</div>");
In this example the files are loaded asynchrounously but keep the specified order. For further reading you can have a look at: http://www.html5rocks.com/en/tutorials/speed/script-loading/
One approach would be to to load the first file(1.js) asynchronously, and then dynamically add the second script as mentioned in the other answer, by making it to load asynchronously.
Load first file:
<script async src="/Content/Scripts/1.js"></script>
Then in the first file, include the following at the bottom,
var script = document.createElement('script');
script.src = "/Content/Scripts/2.js";
script.async = true;
document.head.appendChild(script);
Hope this helps :)
Regular asynchronous scripts doesn't support load ordering. BTW, ES2015 and above have the import syntax to asynchronously-load script files in order:
import x from "x.js";
import y from "y.js";
Or you can also use the programmatic API:
Promise.all([System.import("x.js"), System.import("y.js")]).then(() => {
// Do stuff once they've been already loaded...
});
If you want to use these features today, you should take a look at:
JSPM (which uses SystemJS as polyfill to ES2015 module loading).
Babel transpiler to compile ES2015 to ES 5.x.
Another way could be to use another attribute to store the second's source content
<script async src="/Content/Scripts/1.js"></script>
<script id="second" async data-src="/Content/Scripts/2.js"></script>
and inside the first script after you are finished with the dependent code between the 2 files(if any), write
var script = document.getElementById('second');
script.setAttribute("src",script.getAttribute("data-src"));
script.removeAttribute("data-src"); //Totally upto you if you want to remove it
Compared to the other solutions, this provides you more flexibility in placing the tag anywhere.
Going from "Abhishek singh" -s answer I created some snippet myself.
You can have as many js files to execute consecutively as you need.
html looks like this:
<title>Script execution sequence</title>
<body>
<script async data-ascript-id='2' data-src='second.js'></script>
<script async data-ascript-id='1' data-src='first.js'></script>
<script async src="pixel.js"></script>
</body>
pixel.js:
console.log("Pixel script executed");
var aon_scripts = document.querySelectorAll('[data-ascript-id]');
if(aon_scripts.length > 0){
var next_script = document.querySelectorAll('[data-ascript-id="1"]')[0];
console.log("pixel.js found next script");
next_script.setAttribute("src", next_script.getAttribute("data-src") );
}
first.js:
console.log("Next script executed");
var current_script = document.currentScript;
var this_script_data_id = current_script.getAttribute("data-ascript-id");
if(aon_scripts.length > parseInt(this_script_data_id) ){
console.log("There is more script");
var next_script = document.querySelectorAll('[data-ascript-id="'+
(parseInt(this_script_data_id) + 1) +'"]')[0];
next_script.setAttribute("src", next_script.getAttribute("data-src") );
}
else{
console.log("No more scripts to execute");
}
second.js and all from there will have the same code as first.js.
Output when I have first.js and second.js the same:
pixel.js: Pixel script executed
pixel.js:11 pixel.js found next script
first.js:1 Next script executed
first.js:9 There is more script
second.js:1 Next script executed
second.js:18 No more scripts to execute
If anyone wants a detailed explanation please let me know.

Load js Asynchronously But Execute Synchronously

The Scenario is that I have lots of js files. Different files are called depending on different platforms.
So I have a question as I want to load files Asynchronously. But the Execution of these files should be done synchronously. Currently I am doing this function call
function loadScriptJs(src) {
Console.log(' Call to file '+src);
var file = document.createElement('script'),
head = document.getElementsByTagName('head')[0];
file.type = 'text/javascript';
file.setAttribute("defer","defer");
file.setAttribute("src",src);
head.appendChild(file);
file.onload = function() {
console.log(src + " is loaded.");
};
}
Now I have a array which has a list of js files being called. I loop through the array and call this function for each src.
I have used document.ready function with in each of the js file. Here I have checked the dependencies of the files and made the sequence optimized. ie The dependent files are loaded later (come later in the array) and the more Parent like files come earlier.
Example:- If I have files A1,B2,C3,D4,E5 ..... like wise. My array is ['js/A1.js','js/B2.js','js/C3.js'...]. When I run this through the loop.
I get the consoles of 'Call to file [filename]' is the same sequence as in the array but consoles of '[filename] is loaded' are not coming in the intended sequence.
Yes I cannot control the loading as its over the internet. But I want to the execution to be synchronous. ie Begin all file execution when the last file completes loading.
Can anyone provide me any advice, suggestion or idea for it.
Dynamically inserted scripts may not adhere to the defer attribute. script tags have an async attribute. By default it's true. Explicitly set it to false.
file.setAttribute("async", "false");
or in plain HTML
<script type="text/javascript" async=false src="A1.js"></script>
You might also need to remove the document.ready method in each script and start the script's logic at the top level, appending each JS script to the bottom of the body tag instead of the header. If multiple scripts register for the ready event before it's fired you do not know what order they will be called in.

How to include jQuery in remote Javascript file only if site doesn't have jQuery?

I have a script (embed.js) that is being included from other websites/domains. Ex:
<script type="text/javascript" src="//mydomain.com/embed.js"></script>
The code in this script relies on jQuery, but obviously not every website uses jQuery. So, I put this code in the embed.js script at the top:
if (typeof jQuery == 'undefined') {
var jq = document.createElement('script');
jq.type = 'text/javascript';
jq.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js';
document.getElementsByTagName('head')[0].appendChild(jq);
jQuery.noConflict();
}
But, I get an error about "jQuery" not being defined for the noConflict() line. It is indeed adding the jQuery tag/code to the page, but for some reason after that appendChild() line, the following noConflict() line doesn't recognize jQuery.
How do I only include jQuery on a page if it's not already on it, through a remote Javascript file, and then in that same Javascript file use that jQuery I just included? (This all needs to be through one file.)
You are getting the error because the method you're using for loading jQuery loads it asynchronously, thus jQuery is not yet loaded when you try to execute the .noConflict() line.
You would have similar problems if your code is also trying to use jQuery as it initializes (if your code isn't also loading asynchronously).
If you need to load jQuery synchronously (simpler solution, but probably not preferred), then you can use document.write() to actually write the script tag that would load it and the browser will process that synchronously.
You can also load jQuery in a way that will notify you when it is actually loaded at which time you can run the .noConflict() line and then call your own initialization code that uses jQuery. This is probably the most self-contained mechanism and avoids document.write() which can slow down the loading process some in modern browsers (use of document.write() in some circumstances prevents some loading optimizations).
For example, you could load jQuery with this function and then it would call your callback when it was loaded successfully:
function loadScript(sScriptSrc, oCallback) {
var oHead = document.getElementsByTagName('head')[0];
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = sScriptSrc;
// most browsers
oScript.onload = oCallback;
// IE 6 & 7
oScript.onreadystatechange = function() {
if (this.readyState == 'complete') {
oCallback();
}
}
oHead.appendChild(oScript);
}
loadScript("jquery.js", function() {
jQuery.noConflict();
// call your own initialization function that uses jQuery here
});

Evaluation order of injected scripts

I'm writing a Chrome extension which injects some JS onto a page. My JS is stored in script.js. I use a contentscript.js to actually do the injecting (as is recommended in this SO answer). This all works fine.
My script.js uses jQuery, and therefore needs jquery.js and a bunch of plugins to be injected too. So my contentscript.js looks like this:
var a = document.createElement('script');
a.src = chrome.extension.getURL("jquery.min.js");
a.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(a);
...4 more similiar plugin script injections...
var s = document.createElement('script');
s.src = chrome.extension.getURL("script.js");
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
The sequential order in script.js is the order they must be evaluated in.
My problem is that depending on how it happens to load, I seemingly have no guarantee that jquery.min.js gets evaluated before my script.js, leading to errors like "$ undefined" and "Object does not have ... method" (for the plugins).
FYI, I know it's not some huge blocking issue because when they are all cached and load in the correct order, my extension works perfectly. That's what leads me to believe it's the loading that is the problem.
How do I go about enforcing the order I need on how my scripts are evaluated?
I hope you're not literally chaining the loads together by actually writing out the same code five times, nested five levels deep. This can be solved in a pretty straight-forward manner with a queue and a single function:
function loadNextPlugin(plugins) {
// "pop" the next script off the front of the queue
var nextPlugin = plugins.shift();
// load it, and recursively invoke loadNextPlugin on the remaining queue
var tag = document.createElement('script');
tag.src = chrome.extension.getURL(nextPlugin);
tag.onload = function() {
if (plugins.length)
loadNextPlugin(plugins);
}
(document.head||document.documentElement).appendChild(a);
}
loadNextPlugin(["jquery.min.js", "script2.js", "script3.js", "script.js"]);
Chain the loading of each plugin, by loading the next plugin in the onload handler of the previous one.
Set async=false on the scripts you inject.
This is the spec-compliant way to have scripts loaded in any order, but executed in the order they're added.
From Deep dive into the murky waters of script loading:
[
'1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
Spec says: Download together, execute in order as soon as all download.
Firefox < 3.6, Opera says: I have no idea what this “async” thing is, but it just so happens I execute scripts added via JS in the order
they’re added.
Safari 5.0 says: I understand “async”, but don’t understand setting it to “false” with JS. I’ll execute your scripts as soon as
they land, in whatever order.
IE < 10 says: No idea about “async”, but there is a workaround
using “onreadystatechange”.
Other browsers in red say: I don’t understand this “async” thing, I’ll execute your scripts as soon
as they land, in whatever order.
Everything else says: I’m your friend, we’re going to do this by the book.

Categories

Resources