JavaScript
asynchronous programming
code optimization
script loading
web development

JavaScript How to download JS asynchronously?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Loading JavaScript asynchronously in the browser is mostly about choosing when a script downloads and when it executes. The browser already knows how to fetch scripts in parallel, but the correct attribute or loading pattern depends on whether script order matters. If you mix those behaviors carelessly, you get race conditions instead of faster pages.

Use async When Script Order Does Not Matter

For independent third-party scripts, the async attribute is often the simplest solution.

html
<script async src="/js/analytics.js"></script>

With async, the browser downloads the file in parallel with HTML parsing and executes it as soon as the download finishes. That means:

  • parsing is not blocked during download,
  • execution order between async scripts is not guaranteed,
  • the script may run before the rest of the document is fully parsed.

This is good for standalone scripts such as analytics or ads that do not depend on other scripts.

Use defer When Order Matters

If scripts depend on each other or expect the DOM to be parsed, defer is usually the better choice.

html
<script defer src="/js/vendor.js"></script>
<script defer src="/js/app.js"></script>

Deferred scripts also download in parallel, but execution waits until HTML parsing is complete. Their relative order is preserved, so vendor.js runs before app.js.

That makes defer the usual default for application bundles attached to regular HTML pages.

Load A Script Dynamically With A Promise

If you want to download code only after a user action or only on a specific route, create the script tag manually.

javascript
1function loadScript(src) {
2  return new Promise((resolve, reject) => {
3    const script = document.createElement("script");
4    script.src = src;
5    script.async = true;
6    script.onload = () => resolve();
7    script.onerror = () => reject(new Error(`Failed to load ${src}`));
8    document.head.appendChild(script);
9  });
10}
11
12async function enableCharting() {
13  await loadScript("/js/charts.js");
14  console.log("Charts library loaded");
15}

This approach is useful when the feature is optional and you do not want to pay the download cost on every page view.

ES Modules Are Another Good Option

Modern browsers can load JavaScript modules directly.

html
<script type="module" src="/js/main.js"></script>

Module scripts are deferred by default, support import and export, and are often a better fit than manual script-order management. If your build pipeline already emits modules, this is usually cleaner than juggling several classic script tags.

Choosing The Right Loading Strategy

A practical rule is:

  • use async for independent scripts,
  • use defer for ordered application scripts,
  • use dynamic loading for optional features,
  • use modules when your app is already structured around imports.

The browser can fetch all of these asynchronously. The real decision is execution timing and dependency safety.

Do Not Confuse Downloading With Executing

People often say "download JS asynchronously" when they really mean "do not block the page." Download timing and execution timing are different concerns. A script can download asynchronously and still execute too early for your page logic.

That is why defer exists. It protects execution order and DOM readiness while still allowing parallel downloads.

Common Pitfalls

  • Using async on scripts that depend on each other and then getting random failures.
  • Assuming async waits for the DOM to finish parsing.
  • Dynamically appending script tags without any error handling.
  • Loading optional libraries on every page instead of on demand.
  • Treating classic scripts and module scripts as if they had identical behavior.

Summary

  • 'async downloads and executes independent scripts as soon as they are ready.'
  • 'defer downloads in parallel but waits to execute until HTML parsing is complete.'
  • Dynamic script loading is useful for optional or route-specific features.
  • Module scripts are often the cleanest modern approach for structured applications.
  • The right choice depends on script dependencies, not just on the desire to "load faster."

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.