"Load" event on script with async and/or defer - javascript

When embedding scripts like:
<script src="..." async defer></script>
Is there a way to know when they're finished loading?
Usually when the window.load event is called, one would expect all scripts to be ready as well. But I don't know if that still holds when you load them with async or defer. I've read some docs online but couldn't find anything conclusive on this issue.

Answer:
You could take advantage of the onload event attribute in order to perform some kind of callback once your script is loaded.
Example: In the example html script element below when the script (jquery library from google api) finishes loading asynchronously, an alert will pop up saying 'resource loaded'.
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" async defer onload="alert('resource loaded');">
Note: The src script will load very fast because it is hosted by google so the pop up will most likely appear as soon as the page/DOM has loaded.
Edit: added important information originally from comment.
window.onload waits for everything to load before firing whereas document.onload fires when the Document Object Model (DOM) is ready.
So if you've got async scripts document.onload will execute first while window.onload will wait for those asynchronous scripts to finish loading.
To summarize:
window.onload will take async scripts into account.
document.onload will not take async scripts into account.

Emphasis mine:
Is there a way to know when they're finished loading?
Usually when the window.load event is called, one would expect all scripts to be ready as well. But I don't know if that still holds when you load them with async or defer. I've read some docs online but couldn't find anything conclusive on this issue.
Addressing the points in bold (for specific single scripts you can use their onload events), the TL;DR is:
The document.DOMContentLoaded event will happen after all normal and deferred scripts load and execute, but doesn't care about async scripts.
The window.load event will happen after all normal, async, and deferred scripts load and execute.
Note: A script that has both async and deferred set will act as deferred on legacy browsers that don't support async, and will act as async otherwise. So the safe bet is to think of them as async.
The HTML specification does say this, albeit indirectly. The spec defines three distinct script collections that every document has (I'm naming them S1, S2, and S3):
Each Document has a set of scripts that will execute as soon as possible, which is a set of script elements, initially empty. [S1]
Each Document has a list of scripts that will execute in order as soon as possible, which is a list of script elements, initially empty. [S2]
Each Document has a list of scripts that will execute when the document has finished parsing, which is a list of script elements, initially empty. [S3]
Just above that, in the section about preparing script elements, it details how scripts are distributed to those collections. Generally speaking, during load:
These are placed in S1 (see step 31.2.2):
All async external (scripts with a src) scripts.
All async module (depends on type attribute) scripts.
These are placed in S2 (defer is irrelevant for these) (see step 31.3.2):
Non-async, injected (e.g. by the browser) external scripts.
Non-async, injected module scripts.
These are placed in S3 (see step 31.4):
Deferred, non-async, non-injected external scripts.
All non-async, non-injected module scripts (defer is irrelevant for these).
These are executed synchronously and aren't placed in any of the collections:
Non-deferred, non-async, non-injected external scripts (see step 31.5).
All inline (without a src) scripts (neither async nor defer apply to these) (see step 32).
In simplified terms:
S1 contains all the async external/module scripts.
S2 contains all the non-async injected external/module scripts.
S3 contains all the deferred external/module scripts.
Inline scripts and vanilla external scripts are executed as they're loaded and parsed (as part of the parsing operation).
The HTML spec then goes on to define what happens after parsing is complete, where the relevant parts are, in order:
Change document's ready state to "interactive"; fires document.readystatechange (see step 3)
Execute all scripts in S3 (deferred non-async non-injected) (see step 5)
Queued (will happen >= now): Fire a DOMContentLoaded event on document (see step 6.2)
Wait until all scripts in S1 (async) and S2 (non-async injected) have been executed (see step 7)
Wait until any other load-blocking operations have been completed (see step 8)
Queued:
Change document's ready state to "complete"; fires document.readystatechange (see step 9.1)
Fire a load event on window (see step 9.5)
Fire a pageshow event on window (see step 9.11)
If the document is in some container (e.g. an iframe), fire a load event on the container (see link in step 9.12)
In simplified terms, the events that depend on script executions are:
document.DOMContentLoaded happens after all the deferred scripts are executed.
document.readystatechange ("complete") and window.load happen after all scripts are executed.
window.pageshow also happens after all scripts are executed, although it happens at other times later, too.
If there's a container like an iframe or something, its load event happens after all scripts are executed as well.
Btw, as for scripts with both async and defer set, the part describing these attributes says:
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 blocking behavior that is the default.
For "modern" browsers, I assume the behavior when both are specified is to just adhere to the logic above, i.e. those scripts end up in S1 and defer is essentially ignored.
So uh... yup.

Related

Javascript async defer order of execution in case of multiple files in Single Page Applications

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

What does "async" do when added in a javascript script tag? [duplicate]

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!

How to defer parsing barebone javascript in liferay (if possible) [duplicate]

I have a few <script> elements, and the code in some of them depend on code in other <script> elements. I saw the defer attribute can come in handy here as it allows code blocks to be postponed in execution.
To test it I executed this on Chrome: http://jsfiddle.net/xXZMN/.
<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>
However, it alerts 2 - 1 - 3. Why doesn't it alert 1 - 2 - 3?
A few snippets from the HTML5 spec: http://w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async
The defer and async attributes must
not be specified if the src attribute
is not present.
There are three possible modes that
can be selected using these
attributes [async and defer]. If the async attribute is
present, then the script will be
executed asynchronously, as soon as it
is available. If the async attribute
is not present but the defer attribute
is present, then the script is
executed when the page has finished
parsing. If neither attribute is
present, then the script is fetched
and executed immediately, before the
user agent continues parsing the page.
The exact processing details for these
attributes are, for mostly historical
reasons, somewhat non-trivial,
involving a number of aspects of HTML.
The implementation requirements are
therefore by necessity scattered
throughout the specification. The
algorithms below (in this section)
describe the core of this processing,
but these algorithms reference and are
referenced by the parsing rules for
script start and end tags in HTML, in
foreign content, and in XML, the rules
for the document.write() method, the
handling of scripting, etc.
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 real answer is: Because you cannot trust defer.
In concept, defer and async differ as follows:
async allows the script to be downloaded in the background without blocking. Then, the moment it finishes downloading, rendering is blocked and that script executes. Render resumes when the script has executed.
defer does the same thing, except claims to guarantee that scripts execute in the order they were specified on the page, and that they will be executed after the document has finished parsing. So, some scripts may finish downloading then sit and wait for scripts that downloaded later but appeared before them.
Unfortunately, due to what is really a standards cat fight, defer's definition varies spec to spec, and even in the most recent specs doesn't offer a useful guarantee. As answers here and this issue demonstrate, browsers implement defer differently:
In certain situations some browsers have a bug that causes defer scripts to run out of order.
Some browsers delay the DOMContentLoaded event until after the defer scripts have loaded, and some don't.
Some browsers obey defer on <script> elements with inline code and without a src attribute, and some ignore it.
Fortunately the spec does at least specify that async overrides defer. So you can treat all scripts as async and get a wide swath of browser support like so:
<script defer async src="..."></script>
98% of browsers in use worldwide and 99% in the US will avoid blocking with this approach.
(If you need to wait until the document has finished parsing, listen to the event DOMContentLoaded event or use jQuery's handy .ready() function. You'd want to do this anyway to fall back gracefully on browsers that don't implement defer at all.)
UPDATED: 2/19/2016
Consider this answer outdated. Refer to other answers on this post for information relevant to newer browser version.
Basically, defer tells the browser to wait "until it's ready" before executing the javascript in that script block. Usually this is after the DOM has finished loading and document.readyState == 4
The defer attribute is specific to internet explorer. In Internet Explorer 8, on Windows 7 the result I am seeing in your JS Fiddle test page is, 1 - 2 - 3.
The results may vary from browser to browser.
http://msdn.microsoft.com/en-us/library/ms533719(v=vs.85).aspx
Contrary to popular belief IE follows standards more often than people let on, in actuality the "defer" attribute is defined in the DOM Level 1 spec http://www.w3.org/TR/REC-DOM-Level-1/level-one-html.html
The W3C's definition of defer: http://www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer:
"When set, this boolean attribute provides a hint to the user agent that the script is not going to generate any document content (e.g., no "document.write" in javascript) and thus, the user agent can continue parsing and rendering."
defer can only be used in <script> tag for external script inclusion. Hence it is advised to be used in the <script>-tags in the <head>-section.
As defer attribute works only with scripts tag with src. Found a way to mimic defer for inline scripts. Use DOMContentLoaded event.
<script defer src="external-script.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
// Your inline scripts which uses methods from external-scripts.
});
</script>
This is because, DOMContentLoaded event fires after defer attributed scripts are completely loaded.
The defer attribute is only for external scripts (should only be used if the src attribute is present).
Have a look at this excellent article Deep dive into the murky waters of script loading by the Google developer Jake Archibald written in 2013.
Quoting the relevant section from that article:
Defer
<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 browsers in red say: I have no idea what this “defer” thing is, I’m going to load the scripts as if it weren’t there.
Other browsers say: Ok, but I might not ignore “defer” on scripts without “src”.
(I'll add that early versions of Firefox trigger DOMContentLoaded before the defer scripts finish running, according to this comment.)
Modern browsers seem to support async properly, but you need to be OK with scripts running out of order and possibly before DOMContentLoaded.
Should be also noted that there might be problems in IE<=9 when using script defer in certain situations. More on this: https://github.com/h5bp/lazyweb-requests/issues/42
<script defer> -
As soon as the browser interacts with the script tag with defer
It starts fetching the script file while also parsing the HTML side-by-side.
In this case, the script is only executed once the HTML parsing is completed.
<script async> —
As soon as the browser interacts a script tag with async
It starts fetching the script file while parsing the HTML side-by-side.
But stops the HTML parsing when the script is completely fetched, after which it starts executing the script, post which the HTML parsing continues.
<script> —
As soon as the browser interacts with a script tag
It stops the parsing of HTML, fetches the script file,
In this case, executes the script, and then continues the parsing of HTML.
This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed. Since this feature hasn't yet been implemented by all other major browsers, authors should not assume that the script’s execution will actually be deferred. Never call document.write() from a defer script (since Gecko 1.9.2, this will blow away the document). The defer attribute shouldn't be used on scripts that don't have the src attribute. Since Gecko 1.9.2, the defer attribute is ignored on scripts that don't have the src attribute. However, in Gecko 1.9.1 even inline scripts are deferred if the defer attribute is set.
defer works with chrome , firefox , ie > 7 and Safari
ref: https://developer.mozilla.org/en-US/docs/HTML/Element/script
The defer attribute is a boolean attribute.
When present, it specifies that the script is executed when the page has finished parsing.
Note: The defer attribute is only for external scripts (should only be used if the src attribute is present).
Note: There are several ways an external script can be executed:
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)
If async is not present and defer is present: The script is executed when the page has finished parsing
If neither async or defer is present: The script is fetched and executed immediately, before the browser continues parsing the page

Scripts with asyc attribute still block other script execution

NOTE: I didn't develop this site - I just have to deal with it.
I have a document with LAB.js being used to asynchronously load a series of scripts in the head. Further down I have two more external scripts, one further down in the head, and the other at the top of the body.
Those two scripts are often unreachable. They're both marked as 'async' and don't block the document from loading. HOWEVER - they do evidently block the other scripts from executing.
To be clear, the first series of scripts are downloaded immediately, but won't seem to execute until the other two async scripts are downloaded.
The site isn't public, and this type of issue can't be created in jsfiddle. A crude illustration:
<!doctype html>
<head>
<!-- This script asynchronously loads many others -->
<script src="/js/LAB.js"></script>
<script src="slow-server/js/slowscript1.js" async></script>
</head>
<body>
<script src="show-server/js/slowscript2.js" async></script>
</body>
Info on Lab.js: http://labjs.com/
LAB will prepend several other script tags to the head to be loaded asynchronously. They're all downloaded right away, but will not execute until the "slow-server" scripts respond. Sometimes takes a while. This seems to me incorrect behavior for scripts with the async attribute (and I'm testing in Chrome). Is there something I'm missing?
From this MDN page:
There are three possible modes that can be selected using these
attributes. If the async attribute is present, then the script will be
executed asynchronously, as soon as it is available. If the async
attribute is not present but the defer attribute is present, then the
script is executed when the page has finished parsing. If neither
attribute is present, then the script is fetched and executed
immediately, before the user agent continues parsing the page.
So, when a script has async, it will be fetched immediately from the server, but DOM parsing will continue in parallel. That part is easy and clear.
Then, as soon as the async script has been fetched, it will be executed. This execution may be before or after the DOM has finished loading depending purely upon timing of how things took to load/parse and it may be before or after other non-async scripts in the page have run.
Note, that because javascript in a browser is single threaded, once this async script starts to execute, no other scripts will run until this script finishes, even though it was "async". Async on the scripts allows the parsing of the DOM to continue (rather than block), but it does not keep the script from executing once it is available and once it executes, other processing has to wait for it to complete executing.
If you want the script to NOT execute until after the DOM and any regular scripts in the DOM have loaded and run, then you should use "defer", not "async".
FYI, if you want to wade through the actual specifications for all this it's here.
If you want a more detailed explanation of how all this applies to your exact situation, you will have to at least give us a pseudo-code representation of what scripts you're asking about as your words aren't clear enough to follow.

load scripts asynchronosly and have a fallback

My objective here is to load scripts asynchronously when the browser supports defer or async.
If the browser supports neither I don't care about asynchronous loading (not my bad).
I want to make sure that any script is only executed when the prerequisites for it are fulfilled e.g. jQuery loaded.
I want to load my scripts while other scripts are being loaded (only if the browser supports defer or async).
I want to do this using only the browsers' API. I don't want the browser to load any reliable (or not) scripts that do that for me no matter how small they are.
This must work with IE8+, Gecko v.1.9.1+ (e.g. firefox 3.5.* or firefox 9.0+), webkit (e.g. chrome), presto (e.g. Opera). For the ones I didn't mention the version, I mean the latest stable version.
If possible I don't want any non easy scripts. I just need something simple to do this job. This means:
If possible, I don't want stuff like AJAX calls or fancy Objects with some methods to do some workarounds like I've seen in other pages. Those are to force async loading of the script in browsers that do not support async or defer
I repeat: I don't need any fancy things to make a script asynchronous. If the browser does not support defer or async I don't care. I just care is that the script is loaded so that each part is executed after its prerequisites are met and use async or defer if the browser supports it.
First, using a library such as jQuery makes this whole process infinitely easier, and reliable across browsers. It may increase the download size of your pages (by a very small amount) but the speed gained by efficient script loading/executing will nearly always outweigh that.
Regarding script async and defer attributes:
async="async": on a script tag is not supported at all by IE8/9, script executes immediately (which is ok according to your question).
defer="defer": on a script tag will begin loading after everything in the order the defer scripts appear in the HTML, prior to DOM Ready. BUT, on Firefox, scripts will often execute AFTER dom ready. This difference makes defer unreliable as a means of ensuring that scripts are loaded before executing functions after dom ready.
General Guidelines when not using jQuery:
If a script has downstream dependancies you have to place it as a standard script tag at the end of the body tag and have your inline tags all execute after document ready. Otherwise there is no guarantee that the script will be executed prior to the execution of the dependencies. Firefox is the main issue here, a "defer" script may not have finished even after DOM ready.
If a script has no downstream dependnacies, then place it at the end of the body tag, and use async="async" attribute on the script tag. IE will render it immediately and the others will render it when they receive it.
General Guidelines when using jQuery:
Place only jQuery in your <head>.
Execute all other scripts as $.getScript().
If a script needs to execute ASAP (such as analytics) use a $.getScript at the top of the body (this will be a non-blocking request, but will process as soon as the client receives the file).
If a script can wait till DOM ready, wrap the $.getScript() call in $(function() {});
If a script has many downstream dependancies, have each one register itself to the callback function for a specific script.
$(function() {
$.getScript("script.js", function() {
for(var i = 0; i < myCallbacks.length;i++) {
myCallbacks[i]();
}
});
});

Categories