The Google Maps javascript does some heavy DOM manipulation. Even so, the fine docs suggest to load it with the defer flag:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>
Why would the defer flag be suggested for a script that performs DOM manipulations? I ask to learn both about the defer flag and to learn about the Google Maps API as I seem to have a misunderstanding about what one of them is doing.
Normally, a script tag tells the browser to stop parsing the HTML, fetch the script, run it, and only then continue parsing the HTML. This is because the script code may use document.write to output to the HTML token stream.
async and defer are both mechanisms for telling the browser that it's okay to go ahead and keep parsing the HTML in parallel with downloading the script file, and to run the script file later, not right away.
They slightly different, though; this diagram from the script section of the WHAT-WG version of the HTML spec is useful for envisioning the differences:
Full details in the linked spec above, but in brief, for "classic" scripts (the kind you're used to; but module scripts are coming soon!):
Both async and defer allow the parsing of the HTML to continue without waiting for the script to download.
defer will make the browser wait to execute the script until the parsing is complete.
async will only make the browser wait until the script download is complete, which means it may run the script either before parsing is complete or afterward, depending on when download finishes (and remember it could come from cache).
If async is present and supported by the browser, it takes precedence over defer.
async scripts may be run in any order, regardless of the order in which they appear in the HTML.
defer scripts will be run in the order they appear in the HTML, once parsing is complete.
async and defer are well-supported in even semi-modern browsers, but are not properly supported in IE9 and earlier, see here and here.
Why would the defer flag be suggested for a script that performs DOM manipulations?
Two reasons:
It allows the parsing to continue while the script is downloaded, and
It means the script isn't run until parsing is complete.
If you didn't use defer and you placed your script tags non-optimally, using defer helps the API script behave properly by letting the browser finish building the DOM before the script tries to manipulate it.
A lot of people still put script tags in the head section of the document, even though that's usually the worst place to put them unless you use defer (or async). In most cases, the best place (unless you have a reason to do something else) is at the very end, just before the closing </body> tag, so that A) Your site renders quickly, without waiting for scripts; and B) The DOM is fully built before you try to manipulate it. Recommending defer may be saving them support hassles from people putting their script tags too early in the HTML.
The google maps examples use both async and defer flags.
The async flag allows the script to load in parallel to the DOM
parsing, and to execute as soon as the API is ready.
The defer flag allows the script to load in parallel to the DOM
parsing, but guarantees that the script will not execute until the
DOM is finished parsing.
async is supported by modern HTML5 browsers, while defer support is universal. When the tags are used together, defer is just a fallback for older browsers, and will be ignored if async is supported.
In these simple examples, either async or defer will work, though neither are necessary. In this case it's for performance only.
Refs:
Speed up Google Maps(and everything else) with async & defer
async vs defer attributes - Growing with the Web
Related
I'm using polyfill.io to polyfill Promise and fetch for older clients. On their website they recommend using a script loader or their callback to make sure the script has loaded completely before running the modern code:
We recommend the use of the async and defer attributes on
tags that load from the polyfill service, but loading from us in a
non-blocking way means you can't know for certain whether your own
code will execute before or after the polyfills are done loading.
To make sure the polyfills are present before you try to run your own
code, you can attach an onload handler to the https://cdn.polyfill.io
script tag, use a more sophisticated script loader or simply use our
callback argument to evaluate a global callback when the polyfills are
loaded:
However, shouldn't setting defer on both scripts already guarantee that they are loaded async but still in the order in which they appear in the document (unless the browser doesn't support defer)?
<script src="https://cdn.polyfill.io/v2/polyfill.min.js" defer></script>
<script src="modernscript.js" defer></script>
According to MDN documentation defer attribute just defines a point of page loading time when script loading will occur.
From documentation that you've citated it can be seen:
To make sure the polyfills are present before you try to run your own
code, you can attach an onload handler to the https://cdn.polyfill.io
script tag
Since (as pointed into comments to this answer) it can't be clearly seen if defer scripts will be executed (1, 2) and taking in mind possible browser implementation differences - it may be not the best idea to rely on such behavior.
So better way would be either:
to use some script loader (RequireJS for example)
to add proposed onload handler to first <script> tag and create dynamic <script> tag for loading your code inside this handler
to bundle your code together with Promise polyfill (manually or using bundler like webpack) and load as single bundle.
UPDATE: As pointed by #PeterHerdenborg in comment - MDN document now clearly states that:
Scripts with the defer attribute will execute in the order in which they appear in the document.
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
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.
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.
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]();
}
});
});