Reputation: 2791
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.
Upvotes: 40
Views: 43284
Reputation: 18556
My workaround for the lack of readyState
on async scripts only works when targeting browsers with support for ESModules:
Change the <script>
tags from
<script src="/path/to/script" async></script>
to
<script>(window.asyncScripts = window.asyncScripts || []).push(import("/path/to/script"));</script>
then, to await their load, do:
Promise.all(window.asyncScripts).then(() => {
// Your code here
});
You can also use await Promise.all(window.asyncScripts)
if inside an async context.
This should have the same semantics as the <script async>
tag because the import()
function does an async load by default, but it may delay loading of the script by a tiny amount because the browser needs to switch from the parser to the JS executor.
If this is an issue, a <link rel="modulepreload" href="/path/to/script" />
will help.
Upvotes: 0
Reputation: 40315
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:
document.DOMContentLoaded
event will happen after all normal and deferred scripts load and execute, but doesn't care about async scripts.window.load
event will happen after all normal, async, and deferred scripts load and execute.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:
src
) scripts.type
attribute) scripts.src
) scripts (neither async nor defer apply to these) (see step 32).In simplified terms:
The HTML spec then goes on to define what happens after parsing is complete, where the relevant parts are, in order:
In simplified terms, the events that depend on script executions are:
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.
Upvotes: 7
Reputation: 8411
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.
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. Upvotes: 64