javascripthtmlasynchronousdeferred

Can HTML <script defer> execute before <script async>?


I'm asking a question on a subject that has been well explored and there are many near-answers to what I am asking, but I haven't been able to find the exact answer to what is a simple question.

I understand that defer scripts run in the order they appear on the page, but only after the DOM has been built, and I get that async scripts run as soon as is possible, and I know that neither one blocks the HTML parser.

Here's the question: Is there a case in which a defer script can execute before all the async scripts have executed? Essentially, if the HTML parser has parsed the entire document and is ready to run the defer scripts BUT there are still some async scripts that have not loaded yet, will it wait for these async scripts to load (and execute), or will it run the defer scripts?

Thanks!


Solution

  • From a really simple test, it seems that the defer does not wait for the async scripts to load... but (there's always a but) it also seems that it depends on the browser.

    I ran tests on Chrome 41, Firefox 36 and Internet Explorer 11, and got the same results for Chrome and FF (with defer executing before async) but different results on IE (async always executed before defer).

    That could be explained if IE was ignoring the async attribute (parsing and executing the code immediately), but according to W3Schools and MDN, async is supported from IE10. So I guess, that IE process the asynchronous calls in a different way from the other browsers, making it load and execute faster/before the whole page is parsed (then running before the deferred script).

    This is the HTML code that I used for the test:

    <!doctype html>
    <html>
    
        <head>
            <title>Test</title>
            <script type="text/javascript" src="./async.js"></script>
            <script type="text/javascript" src="./defer.js"></script>
        </head>
    
        <body>
            Testing
        </body>
    
    </html>
    

    Now the content of async.js:

    console.log("async");
    

    And the content of defer.js:

    console.log("defer");
    

    If I run it "as is", the console result is:

    async
    defer

    That is expected because the scripts are executed immediately before the browser continues parsing the page.

    Now let's play with async and defer, and see the results:


    Code:

    <script type="text/javascript" src="./defer.js" defer></script>
    <script type="text/javascript" src="./async.js" async></script>
    

    Result:

    defer
    async

    [Note: this was the result on Chrome and Firefox; for IE, the console was async then defer]

    Defer.js executes before async.js. But it could be because async.js is placed later in the code, so let's swap them.


    Code:

    <script type="text/javascript" src="./async.js" async></script>
    <script type="text/javascript" src="./defer.js" defer></script>
    

    Result:

    defer
    async

    [Note: this was the result on Chrome and Firefox; for IE, the console was async then defer]

    Defer.js still executes before async.js, even if async.js has been called before. So answering your question: Yes, there are cases in which a deferred script will execute before all asynchronous scripts are loaded and parsed.


    Code:

    <script type="text/javascript" src="./async.js"></script>
    <script type="text/javascript" src="./defer.js" defer></script>
    

    or

    <script type="text/javascript" src="./defer.js" defer></script>
    <script type="text/javascript" src="./async.js"></script>
    

    Result:

    async
    defer

    This result is expected as in this case async.js is executed immediately, and defer waits for the page to parse (we'll get the same result even if we swap the script position).


    And finally testing an asynchronous script called before one without async/defer (no need to test the other way around, because it would execute immediately, before the call to the async):

    Code:

    <script type="text/javascript" src="./async.js" async></script>
    <script type="text/javascript" src="./defer.js"></script>
    

    Result:

    defer
    async