(function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.src= 'http://yourdomain.com/script.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
})();
I have some js codes,i don't know why it can be async loaded? I add script dom element to html,why this script can be loaded async but other script and html loaded in one thread.
It sounds to me that you might be confused by what the "async" loading does.
All HTML and scripts (including dynamically inserted scripts like in your case) parse and execute in a single thread.
However, fetching (meaning downloading from the web) can be done at the same time as the above thread. The browser will continue fetching external files (CSS, scripts, images, etc.) in parallel. But in the case of scripts, it will pause parsing when it reaches a script tag that is not fully fetched yet, and execute the script once fetched, so that the script has a chance to modify the rest of the page before the browser parses it. You can change that behaviour using async and defer attributes in your script tag. The just described behaviour corresponds to the default values async = false and defer = false.
When async == true, the browser will continue parsing the page while fetching the script in parallel, then pause the parsing when fetch is completed (hence this can happen at any point in parsing the page), and execute the script. Here you can see the effect of the single thread. I think the spec has a good figure to illustrate that.
Now in the case of dynamically inserted scripts like in your case, most browsers will use async = true by default. Refer to that post and MDN notes for further details.
Finally, there is another meaning when referring to "asynchronous" loading: AMD (if you use Require.js for example) typically uses the code that you show to insert script tags dynamically. In this context, the asynchronous is more about the ability to fetch and execute different scripts "on demand", i.e. only when the user requests some extra functionalities (for example you open a form edition, so the page will only fetch and execute the script related to editing at that moment).
Related
I am trying to improve the page load performance of my page, that is implemented on EmberJS.
I am considering using asyc and defer on our Javascript files. All other optimizations have already been done (moving the scripts to the bottom of the page, adding async and defer to analytics tags etc).
Now, as per ember-cli specs, the generated index.html has two script tags - one vendor JS file and one application JS file.
If I am to implement async and defer, I need to ensure that my vendor JS file is loaded before my application JS file, to make sure the latter has all required code to initialize the application.
I understand that the order in which the scripts are fetched and parsed are different when defined with async and defer, as defined here
My question is this:
If you have multiple JS files in the same page, is there a way to fetch and execute them in a stipulated order? I'm looking for something like callbacks/promises in async requests, but in terms of the actual script tag itself.
Things may have changed for the better since this question was first posted, but it seems that in 2019 you can defer your scripts and have them processed in the order the script tags are written in your html document. Adding defer to both your vendor script and your main script will cause them to load in parallel, not block parsing of the html document, and be processed in order on document parse completion.
The 4.12.1.1 Processing model section of whatwg's scripting document goes into quite a bit of detail that I'll try to summarise here:
If the script's type is "classic" (not type="module"), and the element has a src attribute, and the element has a defer attribute, and the element has been flagged as "parser-inserted", and the element does not have an async attribute
then add the element to the end of the list of scripts that will
execute in order as soon as possible associated with the node
document of the script element at the time the prepare a script
algorithm started.
Check out the link for full details, but essentially what it seems to be saying is that deferred scripts will be processed in the order they are parsed in the html document.
MDN agrees:
Scripts with the defer attribute will execute in the order in which
they appear in the document.
One other important point to note (from the same MDN document):
Scripts with the defer attribute will prevent the DOMContentLoaded event from firing until the script has loaded and finished evaluating.
It's also worth noting that neither whatwg nor MDN says anything about placing your script tag in the head or at the bottom of the body of the html document. If all of your scripts have the defer attribute, they will be processed in occurrence order when the html document has completed parsing. Putting the script tags in the header will mean they will start to download early in the html document parsing process, rather than later which is the case when they are placed at the bottom of the body. But of course that also depends on how many other resources you are downloading from the same host in parallel.
Rambling a bit now, but in summary, for best non-blocking performance:
Place all your script tags as early in the html document as possible
Add them in the order that you want them to be processed
Add the defer attribute to all of them (if they don't need to be processed synchronously or as soon as downloaded)
For scripts that need to be processed as soon as downloaded, add the async attribute. HTML parsing will continue while the script is downloading - will pause when the script has finished downloading and while the script is executed - and will resume once the script has finished executing.
For scripts that need to be processed as soon as downloaded, and that have side effects such as modifying the DOM, don't add async or defer. HTML parsing will pause while the script is downloading - will stay paused when the script has finished downloading and while the script is executed - and will resume once the script has finished executing.
Update July 2020:
In Chrome, downloading and parsing of synchronous scripts (those without async or defer) has improved quite a bit. Downloading and parsing are done on separate threads - and the download thread streams the file into the parser as it downloads.
In combination with <link rel="preload"> in your <head>, it's possible that your file could be downloaded by the time the HTML parser reaches your <script> tag - which means it won't need to pause and can execute the script right away:
The image above is taken from the video Day 2: Chrome web.dev Live 2020 - What's New in V8 / Javascript - the section in which they explain updates to downloading and parsing is about 4 minutes long, but well worth the watch.
I can think of two approaches.
a) Do what you said. i.e. have a script tag which has two chained promises inside, each of which creates a new script tag, appends it to the DOM, adds an onload event function which would be the promise's resolve function and lastly sets its src attribute to the resource's URL. When the script from the first promise loads, the second promise should execute and do the same thing.
b) Take the middle road. Have the vendor file in the head, to load synchronously, and have the application file at the very bottom of the document, to load after everything else finished.
In my opinion the first option is an overkill.
EDIT: Example for a)
<script>
var p = new Promise(function(resolve, reject) {
var scriptTag = document.createElement('script');
document.head.appendChild(scriptTag);
scriptTag.onload = resolve;
scriptTag.src = 'URL_to_vendor_file';
});
p.then(function() {
var scriptTag = document.createElement('script');
document.head.appendChild(scriptTag);
scriptTag.src = 'URL_to_application_file';
};
</script>
Note: The example above can be written and without the use of promises
There are so many different ways to include JavaScript in a html page. I know about the following options:
inline code or loaded from external URI
included in <head> or <body> tag [1,2]
having none, defer or async attribute (only external scripts)
included in static source or added dynamically by other scripts (at different parse states, with different methods)
Not counting browserscripts from the harddisk, javascript:URIs and onEvent-attributes [3], there are already 16 alternatives to get JS executed and I'm sure I forgot something.
I'm not so concerned with fast (parallel) loading, I'm more curious about the execution order (which may depend on loading order and document order). Is there a good (cross-browser) reference that covers really all cases? E.g. http://www.websiteoptimization.com/speed/tweak/defer/ only deals with 6 of them, and tests mostly old browsers.
As I fear there's not, here is my specific question: I've got some (external) head scripts for initialisation and script loading. Then I've got two static, inline scripts in the end of the body. The first one lets the script loader dynamically append another script element (referencing external js) to the body. The second of the static, inline scripts wants to use js from the added, external script. Can it rely on the other having been executed (and why :-)?
If you aren't dynamically loading scripts or marking them as defer or async, then scripts are loaded in the order encountered in the page. It doesn't matter whether it's an external script or an inline script - they are executed in the order they are encountered in the page. Inline scripts that come after external scripts are held until all external scripts that came before them have loaded and run.
Async scripts (regardless of how they are specified as async) load and run in an unpredictable order. The browser loads them in parallel and it is free to run them in whatever order it wants.
There is no predictable order among multiple async things. If one needed a predictable order, then it would have to be coded in by registering for load notifications from the async scripts and manually sequencing javascript calls when the appropriate things are loaded.
When a script tag is inserted dynamically, how the execution order behaves will depend upon the browser. You can see how Firefox behaves in this reference article. In a nutshell, the newer versions of Firefox default a dynamically added script tag to async unless the script tag has been set otherwise.
A script tag with async may be run as soon as it is loaded. In fact, the browser may pause the parser from whatever else it was doing and run that script. So, it really can run at almost any time. If the script was cached, it might run almost immediately. If the script takes awhile to load, it might run after the parser is done. The one thing to remember with async is that it can run anytime and that time is not predictable.
A script tag with defer waits until the entire parser is done and then runs all scripts marked with defer in the order they were encountered. This allows you to mark several scripts that depend upon one another as defer. They will all get postponed until after the document parser is done, but they will execute in the order they were encountered preserving their dependencies. I think of defer like the scripts are dropped into a queue that will be processed after the parser is done. Technically, the browser may be downloading the scripts in the background at any time, but they won't execute or block the parser until after the parser is done parsing the page and parsing and running any inline scripts that are not marked defer or async.
Here's a quote from that article:
script-inserted scripts execute asynchronously in IE and WebKit, but
synchronously in Opera and pre-4.0 Firefox.
The relevant part of the HTML5 spec (for newer compliant browsers) is here. There is a lot written in there about async behavior. Obviously, this spec doesn't apply to older browsers (or mal-conforming browsers) whose behavior you would probably have to test to determine.
A quote from the HTML5 spec:
Then, the first of the following options that describes the situation
must be followed:
If the element has a src attribute, and the element has a defer
attribute, and the element has been flagged as "parser-inserted", and
the element does not have an async attribute
The element must be added
to the end of the list of scripts that will execute when the document
has finished parsing associated with the Document of the parser that
created the element.
The task that the networking task source places on the task queue once
the fetching algorithm has completed must set the element's "ready to
be parser-executed" flag. The parser will handle executing the script.
If the element has a src attribute, and the element has been flagged
as "parser-inserted", and the element does not have an async attribute
The element is the pending parsing-blocking script of the Document of
the parser that created the element. (There can only be one such
script per Document at a time.)
The task that the networking task source places on the task queue once
the fetching algorithm has completed must set the element's "ready to
be parser-executed" flag. The parser will handle executing the script.
If the element does not have a src attribute, and the element has been
flagged as "parser-inserted", and the Document of the HTML parser or
XML parser that created the script element has a style sheet that is
blocking scripts The element is the pending parsing-blocking script of
the Document of the parser that created the element. (There can only
be one such script per Document at a time.)
Set the element's "ready to be parser-executed" flag. The parser will
handle executing the script.
If the element has a src attribute, does not have an async attribute,
and does not have the "force-async" flag set The element must be added
to the end of the list of scripts that will execute in order as soon
as possible associated with the Document of the script element at the
time the prepare a script algorithm started.
The task that the networking task source places on the task queue once
the fetching algorithm has completed must run the following steps:
If the element is not now the first element in the list of scripts
that will execute in order as soon as possible to which it was added
above, then mark the element as ready but abort these steps without
executing the script yet.
Execution: Execute the script block corresponding to the first script
element in this list of scripts that will execute in order as soon as
possible.
Remove the first element from this list of scripts that will execute
in order as soon as possible.
If this list of scripts that will execute in order as soon as possible
is still not empty and the first entry has already been marked as
ready, then jump back to the step labeled execution.
If the element has a src attribute The element must be added to the
set of scripts that will execute as soon as possible of the Document
of the script element at the time the prepare a script algorithm
started.
The task that the networking task source places on the task queue once
the fetching algorithm has completed must execute the script block and
then remove the element from the set of scripts that will execute as
soon as possible.
Otherwise The user agent must immediately execute the script block,
even if other scripts are already executing.
What about Javascript module scripts, type="module"?
Javascript now has support for module loading with syntax like this:
<script type="module">
import {addTextToBody} from './utils.mjs';
addTextToBody('Modules are pretty cool.');
</script>
Or, with src attribute:
<script type="module" src="http://somedomain.com/somescript.mjs">
</script>
All scripts with type="module" are automatically given the defer attribute. This downloads them in parallel (if not inline) with other loading of the page and then runs them in order, but after the parser is done.
Module scripts can also be given the async attribute which will run inline module scripts as soon as possible, not waiting until the parser is done and not waiting to run the async script in any particular order relative to other scripts.
There's a pretty useful timeline chart that shows fetch and execution of different combinations of scripts, including module scripts here in this article: Javascript Module Loading.
A great summary by #addyosmani
Shamelessly copied from https://addyosmani.com/blog/script-priorities/
The browser will execute the scripts in the order it finds them. If you call an external script, it will block the page until the script has been loaded and executed.
To test this fact:
// file: test.php
sleep(10);
die("alert('Done!');");
// HTML file:
<script type="text/javascript" src="test.php"></script>
Dynamically added scripts are executed as soon as they are appended to the document.
To test this fact:
<!DOCTYPE HTML>
<html>
<head>
<title>Test</title>
</head>
<body>
<script type="text/javascript">
var s = document.createElement('script');
s.type = "text/javascript";
s.src = "link.js"; // file contains alert("hello!");
document.body.appendChild(s);
alert("appended");
</script>
<script type="text/javascript">
alert("final");
</script>
</body>
</html>
Order of alerts is "appended" -> "hello!" -> "final"
If in a script you attempt to access an element that hasn't been reached yet (example: <script>do something with #blah</script><div id="blah"></div>) then you will get an error.
Overall, yes you can include external scripts and then access their functions and variables, but only if you exit the current <script> tag and start a new one.
After testing many options I've found that the following simple solution is loading the dynamically loaded scripts in the order in which they are added in all modern browsers
loadScripts(sources) {
sources.forEach(src => {
var script = document.createElement('script');
script.src = src;
script.async = false; //<-- the important part
document.body.appendChild( script ); //<-- make sure to append to body instead of head
});
}
loadScripts(['/scr/script1.js','src/script2.js'])
I had trouble understanding how to get an embedded module-script to execute before the onload event happens. The answers above helped a lot but let me add a partial answer about what fixed my particular problem of misunderstanding the "Load and execute order of scripts".
I first used ... which caused an odd problem that it worked when loading the page normally, but not when running it in debugger on FireFox. That made debugging very difficult.
Note: Scripts whose type is "module" always have an implicit "deferred" attribute which means they don't stop the parsing of html, which means the onload-event can happen before the script gets executed. I did not want that. But I did want to use type="module" to make my un-exported JavaScript functions and variables invisible to other scripts on the same page.
I tried different options but thanks to the above answers I gained the insight that if you add the async -attribute to a script of type module it means that the script loads asynchronously BUT once it is loaded it executes immediately.
But in my case this was a script embedded in an HTML page. THEREFORE it meant nothing needed to load "asynchronously". It was already loaded with the page, since it was embedded in it. Therefore it with this change did get executed immediately -- which is what I wanted.
So I think it is worthwhile to point out this specific case because it is somewhat counter-intuitive: To get an embedded script executed IMMEDIATELY you must add the ASYNC attribute to its tag.
Ordinarily one might think that "async" means something happens asynchronously, in indeterminate order, not immediately. But the thing to realize is that "async" causes asynchronous LOADING, but immediate EXECUTION after the loading is complete. And when the script is embedded, no loading needs to be done, and therefore you get immediate execution.
Summary: Use
<script type="module" async> ... </script>
to get a module-script embedded to an HTML-page to execute immediately.
Perfect match for your query!
If none of the solutions worked for you then please refer to the below solution of mine which I have developed from my side.
I was also looking for the solution but after searching a lot, I summarized my code as below which is working perfectly for me!!
This is useful when you want functionality such that after previous script has fully loaded then and then only load next script!
just create a file named jsLoadScripts.js and insert it into the head or at the bottom of the body.
//From Shree Aum Software Solutions
//aumsoftwaresolutions#gmail.com
//script incrementor for array
scriptIncrementor = 0;
//define your script urls here
let scripts = [
"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js",
"jsGlobalFunctions.js",
"jsDateParser.js",
"jsErrorLogger.js",
"jsGlobalVariables.js",
"jsAjaxCalls.js",
"jsFieldsValidator.js",
"jsTableClickEvent.js",
"index.js",
"jsOnDocumentReady.js",
];
//it starts with the first script and then adds event listener to it. which will load another script on load of it. then this chain goes on and on by adding dynamic event listeners to the next scripts!
function initializeScripts() {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = scripts[scriptIncrementor];
document.head.appendChild(script);
script.addEventListener("load", function () {
loadNextScript();
scriptIncrementor++;
});
}
// this function adds event listener to the scripts passed to it and does not allow next script load until previous one has been loaded!
function loadNextScript() {
if (scriptIncrementor != scripts.length - 1) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = scripts[scriptIncrementor + 1];
document.head.appendChild(script);
script.addEventListener("load", function () {
loadNextScript();
scriptIncrementor++;
});
}
}
// start fetching your scripts
window.onload = function () {
initializeScripts();
};
This may cause you some speed related issues so, you can call function initializeScripts() with your custom needs!
If my browser makes a GET to a script src which is currently having a gateway timeout aka 504, why does the browser hang and stop rendering until the response is actually delivered 60 seconds later? Aside from crashing the browser, isn't this the worst thing that could happen to a production javascript application? Is there anything you can do as the app dev to prevent this from blocking the rest of the rendering and script execution?
If the script is inline (e.g. not dynamically loaded) and not marked defer or async, then the script must be processed synchronously in order and the browser cannot proceed without it. Inline <script> tags (without any special attributes) are processed in order as encountered and the browser MUST process them that way.
If you want your page to render without waiting for the script to load, then you can either load it dynamically or you can mark it async or put the <script> tag right before the </body> tag and the page rendering will not wait for it. If using defer or async, you must make sure that no other scripts are dependent upon the loading of this script, otherwise they might run before this one loads.
See these references for more info:
load and execute order of scripts
Script Tag - async & defer
If you are talking about javascript in the DOM within script tags, browsers will always load them synchronously which is why it is important to have the bulk of your JS at the bottom of the page. If this becomes a big issue I would recommend using using an async loading library such as lab.js http://labjs.com/.
If you are getting into more advanced JS and want to utilize something like the AMD pattern for script loading and dependencies you can use http://requirejs.org/.
I have some confusion around the new async attribute to the script element in HTML5 that I hope someone can give a clear answer to.
Browsers are capable of Parallel Connections, therefore images will be downloaded in parallel. But any external javascript is not downloaded in parallel with other external javascript and images. Scripts block page loading until they have been downloaded and executed.
To download a script without blocking the rest of the page loading, the most common technique is to create a script element, like Google Analytics snippet does:
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.src = '...ga.js';
ga.async = true;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
I'm not sure of how that works exactly -
either
the browser parses and renders the page, then once it has finished it notices the DOM has changed, resulting in the ga.js script being downloaded and executed
or
the browser starts downloading the javascript in parallel with other resources.
I think it is the latter.
The new asynchronous Google Analytics snippet includes the HTML5 async attribute in the script element it creates. That will not help the page blocking problem - that has already been solved by the "Script DOM Element" technique. So what does async add to the picture? According to w3schools, "if async is present, the script is executed asynchronously with the rest of the page (the script will be executed while the page continues the parsing)".
And according to Steve Souders site, "the main benefit of this [async attribute] is it tells the browser that subsequent scripts can be executed immediately – they don’t have to wait for ga.js".
So are async and the Script DOM element technique both solving the same problem?
Will work:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>$('body').append('Yey');</script>
Will not work:
<script async src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>$('body').append('Yey');</script>
The async attribute is just a clearer (no ambiguity very straightforward) and cleaner (it will, or is already, part of the respected HTML5 specification) approach to solve the problem. If your site serves scripts from another domain (or CDN) then the async attribute gives you a little reliability (allow the user to at least read the static content) in that the page won't block while a script from a slow (possibly down) remote host is trying to load.
There was a great article from Jake Archibald on html5rocks which addresses this topic.
According to https://www.html5rocks.com/en/tutorials/speed/script-loading/ if a <script> element is added dynamically it may not be executed until DOMContentLoaded is fired. That is, some user agents (e.g. MSIE 10) will wait until DOM is ready before running dynamically added <script> elements.
I guess Google wants to get their analytics code running faster on those user agents and as such they need to add async flag to tell the browser (e.g. MSIE 10) that it's okay to start executing the script as soon as possible. HTML5 compatible browsers would execute as if the async true even if it was not defined so the async=true has been added only to improve performance with non-HTML5 browsers.
setting async attribute to true makes sure that the script is loaded along with the rendering of html in parallel.This is essential because if script is placed at end of body and in html we are using something that depends on javascript code,so it won't be loaded and creates issue
defer can be used but defer just pauses execution of script and renders html
Async scripts are executed as soon as the script is loaded, so it doesn't guarantee the order of execution (a script you included at the end may execute before the first script file )
Defer scripts guarantees the order of execution in which they appear in the page.
I want to load a non-blocking javascript on my page. Before async is supported, the best practice seems to dynamically load it via a simple script.
Here is an example that works fine in which it is inserted before the first <script>:
var myscript = document.createElement('script');
myscript.async = true; // cannot hurt, right?
myscript.type = 'text/javascript';
myscript.src = 'myscript.js';
var node = document.getElementsByTagName('script')[0];
node.parentNode.insertBefore(myscript, node);
I found several versions inserting the script in different places like the end of <head> or the <body>:
document.getElementsByTagName("head")[0].appendChild(myscript);
document.getElementsByTagName("body")[0].appendChild(myscript);
The order seems to matter in some browsers though it is asynchronous.
Are there any difference in terms of browser support? performance? blocking risk?
I don't have any constraint in terms of order (they don't impact each other) but I want to make sure that if my script takes too long to load the page content will still load just fine. I would think the last solution works best but I am not sure of the differences.
You'll want to use something like $script.js: http://www.dustindiaz.com/scriptjs
Appending the scripts at the end of the body is the best solution here. It still allows for loading the DOM without blocking for the script tags. Also if you put your scripts at the end of the document you no longer need to wrap your functions in a DOM ready event because at the moment your scripts start executing the DOM will already be loaded by the browser and you could directly start manipulating it or subscribing to some events.
You could try having a script that waits for the page to be complete and then loads the script that you want to add. Have done this recently and the page loads fine and then a new block appears.
var Widget = {}
Widget.myDocReadyInterval = setInterval(function(){
if (document.readyState === "complete")
{
clearInterval(Widget.myDocReadyInterval);
Widget.startLoading();
}
}, 20);
Widget.startLoading(){
// do what you need here...
}
Your question focuses on the load part, but actually performance can be impacted by different phases:
load
parsing
execution
For this reason adding the script at the end of the body is usually considered the less obtrusive.
To push it even further, you could wait for DOM ready to run your load script. In this case, it won't matter whether you attach the script to the head or the body.
[Edit] Side comment: the head and body tags are not mandatory in html pages. document.getElementsByTagName('script')[0] is a good approach in such edge cases as it guarantees that you'll get an element (there's at least your load script in the page).