DEV Community

Amin
Amin

Posted on • Updated on

Multithreading in JavaScript, sort of

I'm just kidding. JavaScript is single threaded, and I wont use Web Workers here. This was a clickbait. Sorry...

But wait, come back!

What I'll show you is a real game changer for people seeking a solution to lighten the script load on their page. It even works for those of you who don't want to/can't use a web server.

The source-code

<!DOCTYPE html>
<html>
  <body>
    <div id="app"></div>
    <script>
      "use strict";

      function loadScript(src) {
        return new Promise(function(resolve) {
          const script = document.createElement("script");

          script.setAttribute("async", true);
          script.setAttribute("src", src);
          script.addEventListener("load", resolve);

          document.head.appendChild(script);
        });
      }

      async function main() {
        await Promise.all([
          loadScript("https://unpkg.com/vue/dist/vue.js"),
          loadScript("https://unpkg.com/vue-router/dist/vue-router.js")
        ]);

        const { Vue, VueRouter } = window;

        console.log(Vue);        // ƒ Vue (options)
        console.log(VueRouter);  // ƒ VueRouter (options)
      }

      main();
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Explainations

The function

The function I wrote is an asynchronous function. You can tell by the return value of it as being a promise. If you are not familiar with promises yet, I strongly advise you to read the Using Promise guide from Mozilla Developper's documentaton website.

That also means that it won't block the main thread when called in your script. This is ideal for us, as we are great consumers of scripts nowadays.

The main function

The main function is here because we do not have top-level await, yet. The async and await keywords are syntactic sugar to use promise in an imperative style. Again, if you are not familiar with those keywords, you can read a little about it here. I could also have written it that way:

Promise.all([loadScript("..."), loadScript("...")]).then(function() {
  //...
});
Enter fullscreen mode Exit fullscreen mode

The parallel loading

You may be wondering, why I didn't wrote it with a simple for loop? Here is the code I would have wrote if I wanted to use a for loop:

const urls = [
  "https://unpkg.com/vue/dist/vue.js",
  "https://unpkg.com/vue-router/dist/vue-router.js"
];

for (const url of urls) {
  await loadScript(url);
}
Enter fullscreen mode Exit fullscreen mode

But in this case, this has nothing to do with the original code I wrote. This loop will take longer because it has to wait for the first script to load before begining to load the second. Which is not very efficient. The Promise.all will just load them at the same time in parallel. Which is faster of course.

Conclusion

This is a neat little trick if you have to use a lot of script in your page because this can speed up the loading of the page. You'll still be prone to network lag and issues that I didn't cover here. I let that as an exercise for the reader. There is in particular an issue with the Promise.all call when loading buggy scripts.

What do you think? Will you use this trick in the future for your websites? Let me know in the comment section below!

Discussion (0)