Search code examples
javascripthtmlsequenceloading

JavaScript: Execute each script sequentially waiting for previous script to finish


I have 3 scripts that need to execute in order where each next script waits for the previous script to finish executing. Is there a faster more elegant way to write this?

Say for example I had a production html page that needed to load 20 scripts in order.

note: adding defer to each script does not execute the scripts sequentially.

Here is a working example:

index.html

<html>

<head>
    <title>Load scripts in order</title>
    <script>
        const scripts = ["script1.js", "script2.js", "script3.js"]
        for (let i = 1; i < scripts.length; i++) {
            document.head.addEventListener(scripts[i - 1], () => {
                console.log(`finished processing ${scripts[i - 1]}`)
                const nextScript = document.createElement("script")
                nextScript.src = scripts[i]
                document.head.appendChild(nextScript)
            })
        }
    </script>

    <!--load our first script to start sequential loading-->
    <script src="script1.js"></script>
</head>

<body>
    <h1>Hello world</h1>
</body>

</html>

script1.js

const foobar = async () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
        }, 3000)
        setTimeout(() => {
            reject(new Error("did not finish in time."))
        }, 10000);
    })
}

foobar().then(() => {
    console.log("finished loading script1.js")
    document.head.dispatchEvent(new Event("script1.js"))
} )

script2.js

const foobar2 = async () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
        }, 3000)
        setTimeout(() => {
            reject(new Error("did not finish in time."))
        }, 10000);
    })
}

foobar2().then(() => {
    console.log("finished loading script2.js")
    document.head.dispatchEvent(new Event("script2.js"))
} )

script3.js

console.log("hello world")

stack trace:

finished loading script1.js
finished processing script1.js
finished loading script2.js
finished processing script2.js
hello world

Solution

  • you will need to use for loop that support async and await for (const script of scripts) sample is below

    <html>
    
    <head>
        <title>Load scripts in order</title>
        <script>
            async function loadScript(src) {
                return new Promise((resolve, reject) => {
                    const script = document.createElement("script");
                    script.src = src;
                    script.onload = resolve;
                    script.onerror = reject;
                    document.head.appendChild(script);
                });
            }
    
            async function loadScriptsSequentially(scripts) {
                for (const script of scripts) {
                    console.log(`Loading script: ${script}`);
                    await loadScript(script);
                    console.log(`Finished loading script: ${script}`);
                }
            }
    
            const scripts = ["script1.js", "script2.js", "script3.js"];
            loadScriptsSequentially(scripts);
        </script>
    </head>
    
    <body>
        <h1>Hello world</h1>
    </body>
    
    </html>