Skip to main content
← ⚑ JavaScript & the browserΒ·Module C4 Β· Lesson 9
TaskUse top-level await to await Promise.resolve('hello'), assign to const greeting, log it. Then await Promise.resolve(123), assign to const num, log 'num:', num.

Top-Level await in ES Modules: Skip the IIFE Wrapper

75 XP6 min
Theory

The classic workaround

Before 2022 you couldn't await at the top of a file. So everyone wrote IIFEs:

(async () => {
  const r = await fetch("/api/user");
  const user = await r.json();
  console.log(user);
})();

It's just there to create an async scope. Five tokens of pure noise.

Top-level await β€” what every module gets

Inside any ES module (file with import / export, or <script type="module">), you can write:

const r = await fetch("/api/user");
const user = await r.json();
console.log(user);

The module load itself becomes async. Anything that imports this module waits until your top-level awaits finish. That's exactly what you want for "initialize the config before anyone uses it."

The cost

Top-level await delays the module's "done loading" event. If three modules each have a 500ms top-level await, the user sees nothing for 1.5s. So:

  • Use it for initialization that genuinely can't happen later (config from a fetch, WASM module compile, decryption key).
  • Avoid it for "would be nice to prefetch" stuff β€” do that lazily on first call instead.

What it doesn't do

Top-level await doesn't make sibling modules wait on each other unless they directly import each other. ES module loading is still parallel where it can be β€” top-level await just adds a "ready" gate on the importing side.

πŸ”’

Sign up to start coding

Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial β€” card required, cancel anytime.

Sign up β€” free trial β†’

First 10 lessons in each track are free. No card needed for those.

← PreviousNext lesson β†’

Get one Python or web tip a day β€” by email

Short, hand-written, no spam. Unsubscribe in one click.