ClearCrescendo
ClearCrescendo

Reputation: 1155

If the async attribute is used to load non blocking <script>s in the <head> are they guaranteed to be loaded before body.readyState == 'loaded'

Without the async or defer attribute the loading of JavaScript blocks the browser and any scripts loaded in the <head> are always loaded before the dom is loaded and body.readyState == 'loaded'.

My question is specific to the use of the async attribute to allow a non blocking <script> in the <head>. Some browsers can then start rendering the DOM while the javascript is still being retrieved. I have found situations where at least Chrome definitely does render prior to the async JavaScript load in the <head> completing.

Are these async loaded scripts and as a result the <head> guaranteed to be loaded before body.readyState == 'loaded' and traditional dom ready javascript is executed?

I can confirm in waterfalls using Chrome, Firefox and IE11 that in practice in my test cases the onload processing always occurred after all the JavaScript had loaded, and immediately afterwards in some cases, giving the impression that in current browsers the async does not break the assumption that JavaScript has loaded before the body state changes.

This however is anecdotal evidence and what I am looking for is a standard reference or reference/reasoning regarding the browser architecture that gives comfort that for a large javascript loaded in the <head> with async and a small <body> I will not find situations where the <body> completes loading and has a state of loaded before the <head> due to the use of non blocking async script loads.

Upvotes: 2

Views: 931

Answers (2)

ClearCrescendo
ClearCrescendo

Reputation: 1155

readyState is actually implemented by document and as a result the <body> only has a readyState of 'loaded' when the complete document (Both the <head> and the <body>) have loaded. Using the async attribute to load JavaScript in the <head> is safe in that all JavaScript will have loaded before the <body>, or actually anything, appears to be 'loaded'.

When the <body> is asked for its readyState the request is being responded to by its parentNode which is the document. Document implements readyState.

The code initiating the on-load processing was:

var body = document.getElementsByTagName('BODY')[0];
if (body && body.readyState == 'loaded')
    { ... }

So yes, it is guaranteed that even async JavaScript files will be fully loaded before any on-load processing that tests a <body>'s readyState receives 'loaded'.

It however, would be far clearer, just to ask the document for its readyState.

Many thanks to @Bergi for pointing out, in a comment to the question, that it is actually document that is implementing readyState and for locating the documentation:

https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState

Upvotes: 2

devconcept
devconcept

Reputation: 3685

There are many things to consider when you want to know wich part of your html will be rendered first.

  • Size of the script being loaded
  • Size of the rest of the document
  • What tecniques the browser uses to optimize the loading of content

Some browsers use speculative parsing to continue loading even if it finds a blocking script. Depending if the speculative loading succeeds the browser will use the content already retreived instead of continuing rendering the requested document.

You can read more about speculative parsing in the mozilla docs

If for example you have a huge script, say a complete spa aplication using an async attribute, is very likely that your document will render before the loading of the script finishes but this does not mean a loaded event will be triggered.

The readyState of the body it will be changed to 'loaded' after all assets (scripts, styles, etc) are loaded. Read loaded as downloaded or failed (also includes timeout). Your page is also considered one of this assets so this event only happens if all this conditions are true. The head of your document is also part of your page and needs to be downloaded before the documents is declared as ready and the onload event is triggered.

On the other side javascript execution is a different matter. Using the async attribute only garantees that the browser will continue to parse the document and execute the script after it has finished downloading which could be in any moment. Your document might be already downloaded or not, that depends on the ammount of content remaining. Also there is a chance that there are already other scripts being executed wich will delay the execution of your script.

The defer attribute is not a standard and can not be used as a garantee either.

From the docs

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. 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.

Finally you should note that the async attribute is not supported in all browsers and will have no effect if your page is being requested from an older browser.

Upvotes: 0

Related Questions