skip to content

For an external classic script in HTML — <script src="a.js"></script> — what is the difference between adding the async attribute and adding the defer attribute, both in when the script executes and in what ordering guarantees you get across several such scripts?

level: middleimportance: must knowfreq 88%

answer

  1. both download in parallel
  2. the difference is when it runs
  3. one interrupts, one waits for parsing
  4. order guaranteed on exactly one of them
  5. analytics versus application bundle

basics

~10 s

Both download in parallel with parsing. An async script executes the moment its download finishes, interrupting the parser, in unpredictable order. A defer script executes only after parsing completes, in document order.

solid answer

~40 s

Both attributes stop the script from blocking the fetch: the download happens in parallel with HTML parsing either way. The difference is execution. `async` means "run it the instant it arrives" — the parser is interrupted at whatever point the download happens to complete, and with several async scripts the execution order is whatever the network delivers first, so it is effectively unpredictable. `defer` means "run it after the document is fully parsed" — deferred scripts execute in document order, one after another, before `DOMContentLoaded` fires, and they never interrupt parsing. So `async` suits independent, order-free scripts like an analytics tag, and `defer` suits application code that depends on other scripts or on the DOM. Both attributes are ignored on inline classic scripts, and if you specify both, `async` wins.

code

html · 8 lines
html
<head>
  <!-- independent, order-free: safe as async -->
  <script src="analytics.js" async></script>

  <!-- dependent pair: must stay ordered, so defer both -->
  <script src="lib.js" defer></script>
  <script src="lib-plugin.js" defer></script>
</head>

go deeper

for a junior

Recall that both attributes let the download happen alongside parsing, and that defer is the safe choice for your app bundle while async suits a standalone tag like analytics.

for a middle

Explain the execution timing precisely: async runs on arrival and interrupts the parser, defer runs after parsing in document order. State the ordering guarantee explicitly — that is what the question is really testing.

for a senior

Be ready to audit a real page's script tags and justify each choice, including spotting an async tag whose script has a hidden dependency and explaining why the resulting failure is intermittent.

for a principal

Own the loading contract across teams: which categories of script may be async, how third-party tags are constrained, and how you prevent an ordering assumption from silently entering a shared template.

## Three loading modes, not two An external classic script has three behaviours available: ```html <script src="a.js"></script> <!-- blocking: fetch and run with the parser stopped --> <script src="a.js" async></script> <!-- fetch in parallel, run the moment it lands --> <script src="a.js" defer></script> <!-- fetch in parallel, run after parsing, in order --> ``` Everything below is about the second and third. ## What both share Both attributes decouple the *download* from parsing. The browser starts the request when it encounters the element and keeps building the DOM while the bytes are in flight. Neither attribute has any effect on an inline classic script — `<script async>code()</script>` with no `src` runs immediately and synchronously, exactly as if the attribute were absent. ## async: run as soon as available An `async` script executes at the first opportunity after its download completes. If the document is still being parsed at that moment, the parser is paused, the script runs, and parsing resumes. So `async` removes the *network* stall but not necessarily the *execution* stall — it just moves it to an unpredictable point. The consequence people miss is ordering. Given ```html <script src="jquery.js" async></script> <script src="jquery-plugin.js" async></script> ``` the plugin can and will sometimes run first, because it is smaller and finishes downloading sooner. There is no guarantee tying execution order to document order, to file size, or to anything else you can control. Async scripts also make no promise about `DOMContentLoaded`: one may execute before it or after it, and it may find the element it wants missing. That makes `async` correct for exactly one shape of script: self-contained, dependency-free, and indifferent to whether the DOM is ready — an analytics beacon, an error reporter, a feature-flag ping. ## defer: run after parsing, in order A `defer` script is added to a list that the browser executes once parsing of the document is complete. The guarantees are strong and worth memorising: - Deferred scripts never interrupt parsing at any point. - They execute in **document order**, regardless of which finished downloading first — a slow first script holds back a fast second one. - They all execute before `DOMContentLoaded` fires, so that event is a reliable "all deferred code has run" signal. - The DOM is fully built when they run, so querying elements is safe. That combination is why `defer` is the sensible default for application code. It gives you early discovery (the tag can sit in `<head>`), parallel download, ordered execution, and a ready DOM. ## Choosing between them Ask two questions. *Does this script depend on another script or on the DOM?* If yes, it cannot be `async`. *Does anything depend on this script running before something else?* If yes, it cannot be `async` either. Everything that survives both questions can be `async`; everything else gets `defer`. A useful sanity check: two `async` tags where one is a library and the other its plugin is a bug that will look like a flaky, environment-dependent "undefined is not a function" in production and pass every time on a warm local cache. ## Edge cases interviewers probe **Both attributes on one tag.** `<script src="a.js" async defer>` behaves as `async` in any browser that supports `async`; `defer` was left in as a fallback for ancient browsers that supported only `defer`. In practice, writing both today just means `async`. **Injected scripts.** Adding `async` or `defer` to markup is not the same as creating an element in JavaScript — a script element created with `document.createElement` has its own default behaviour, which is a separate topic. **Blocking is still available and sometimes wanted.** A tiny inline configuration snippet that must run before a deferred bundle sees it is legitimately synchronous. ## The one-sentence version `async` = parallel download, execute immediately, order unspecified; `defer` = parallel download, execute after parsing in document order. Saying only "both load in the background" is the answer that fails, because it omits the only part the interviewer is testing.

  • You have a library and a plugin that depends on it, both external files. Can you mark both async?
    No. Async execution order follows download completion, so the plugin can run before the library and throw. Use `defer` on both — deferred scripts execute in document order — or bundle them into one file, or load the plugin from the library script's `onload`.
  • What happens if a script tag carries both async and defer?
    `async` wins. Any browser that understands `async` uses it and ignores `defer`; the pair only existed so that very old browsers supporting `defer` alone would still avoid blocking. Writing both today is just a verbose way of writing `async`.
  • Which of the two gives a reliable relationship with DOMContentLoaded?
    `defer`. All deferred scripts run, in order, after parsing finishes and before `DOMContentLoaded` is dispatched. An `async` script may execute before or after that event depending purely on when its download completes, so it cannot assume the DOM is complete.
  • Does async remove the risk of the script stalling the page?
    Only the network half. When an async script's download finishes mid-parse, the parser is paused while the script executes, so a large async bundle still occupies the main thread — just at an unpredictable moment. It moves the stall rather than eliminating it.

saying these in an interview costs you the question

  • Says async and defer are basically the same
  • Claims async scripts run in document order
  • Thinks defer just delays the download
  • Believes async scripts never interrupt parsing
  • Uses async for a library and its dependent plugin

context