javascriptjqueryasynchronous

Asynchronous scripts execute after deferred


Ideally, I would like to have all scripts load asynchronously but in the HTML order. As far as I know, async doesn't work that way.

With the following attributes, the general.min.js and funcs.min.js scripts do not run since there is some jQuery within them.

<script type="text/javascript" src="scripts/jquery-3.2.1.min.js" async="async"></script>
<script type="text/javascript" src="scripts/general.min.js" defer="defer"></script>
<script type="text/javascript" src="scripts/funcs.min.js" defer="defer"></script>

I understand that the attribute only affects the download and not the execution of the scripts.

How, then, is it possible to maintain the execution order after download?


Solution

  • If you want your scripts to download asynchronously yet execute in order, use defer. Rendering won't be blocked because script execution won't start until the document is parsed.

    On the other hand, async will also download scripts asynchronously, but each script it executed immediately after downloading finishes. So if you include a small script below a large script, the small script will download quicker and be executed first.

    EDIT #2 (EDIT #1 was replaced by #2 for clarity)

    The using defer scripts makes a lot of sense when all scripts are loaded in the <head> from external sources. The download is async, DOM parsing isn't blocked, and the execution order is maintained. Yay! I will call these the deferred scripts.

    However, inline scripts (<script>...</script>) in the <body> are downloaded and executed synchronously during DOM parsing, so they won't have access to the deferred scripts (because those don't execute until after DOM parsing).

    What if the inline scripts need access library scripts, such as jQuery or lodash? Obviously, those library scripts must be fully downloaded and executed before the inline scripts run. This is done by putting the library scripts in the <head> without the use of defer. This makes them execute immediately, in order. Note that the deferred scripts can also access the library scripts because they are loaded even after the the inline scripts!

    Phew! Here's an example:

    <head>
        <!-- Executed last: deferred scripts -->
        <script src="scripts/general.min.js" defer></script>
        <script src="scripts/funcs.min.js" defer></script>
        
        <!-- Executed first: library scripts, such as jquery, lodash, etc.. -->
        <script src="scripts/jquery.min.js"></script>
    </head>
    <body>
        <!-- Executed second: inline scripts -->
        <script>
            $.ready(console.log);
        </script>
    </body>
    

    Just one addition: deferred scripts don't need onload listeners (or $.ready, etc..) because the DOM is already loaded!