0

There are several questions about this topic like the following one: Is the "async" attribute/property useful if a script is dynamically added to the DOM?

But all of these questions only focus on adding one script-tag to the DOM or only script-tags which do not depend on each other.

We use Three.js and their examples. Since Three.js is a sophisticated project which is around since a long time not the whole examples etc are build around ES modules. Therefore we load these example scripts dynamically by adding a script-tag. As mentioned in other questions adding a script-tag dynamically sets it to async. But you can override this behavior. This is quite nice for us because if we set the script to async:false the execution order is preserved (which helps us keeping complexity low), what we do is basically the following (real-world code is much more involved but here I want to show the gist):

const attatch = (url, options = {}) => {
    return new Promise((resolve, reject) => {
        const element = document.createElement('script');
        element.async = options.async || false;
        element.src = url;
        if (options.id) {
            element.id = options.id;
        }
        element.onload = () => {
            resolve();
        };
        element.onerror = (error) => {
            reject(error);
        };
        starts.push(performance.now());
        document.getElementById('container').appendChild(element);
    });
};

Promise.all([
    attatch('./three/lib/rthree.js'),
    attatch('./three/lib/postprocessing/EffectComposer.js', {id: 'effect-composer-js'}),
    attatch('./three/lib/postprocessing/RenderPass.js', {id: 'render-pass-js'}),
    attatch('./three/lib/postprocessing/ShaderPass.js', {id: 'shader-pass-js'}),
    attatch('./three/lib/shaders/LuminosityHighPassShader.js', {id: 'luminosity-high-pass-shader-js'}),
    attatch('./three/lib/postprocessing/UnrealBloomPass.js', {id: 'unreal-bloom-pass-js'}),
    attatch('./three/lib/postprocessing/OutlinePass.js', {id: 'outline-pass-js'})
]).then(() => {
    // Run 3D content now
});

I did some benchmarking and I didn't discover any significant difference between setting async to false. But I'm not sure if I didn't miss anything. Especially because I also want the browser to construct the UI while loading the scripts and then start the Three.js scene if everything is ready. Since it's not that easy to benchmark this change in our real world app I created an example project. Since I found no real difference it makes me wonder if I didn't measure correctly or if there are really no performance differences.

Do you think there are significant performance improvements in using async:true over async:false or is this at best a micro-optimization? One side note: we are only interested in results for modern browsers

1 Answers1

0

Your results make sense to me. async will tell the browser to keep parsing the HTML when it's reaching a <script> tag and execute it as soon as the file is ready. Since you are already loading your Javascript files asynchronously with Promises, this won't block the browser to continue parsing your HTML so whether you use async or not doesn't really matter. I would even remove it to simplify the source code.

Pingolin
  • 3,161
  • 6
  • 25
  • 40