Reputation: 21852
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:
script
tag itself.Upvotes: 6
Views: 5843
Reputation: 764
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 theDOMContentLoaded
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:
defer
attribute to all of them (if they don't need to be processed synchronously or as soon as downloaded)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.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.
Upvotes: 7
Reputation: 9358
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
Upvotes: 1