Explain Codes LogoExplain Codes Logo

How can I use async/await at the top level?

javascript
async-programming
top-level-await
javascript-features
Alex KataevbyAlex Kataev·Feb 11, 2025
TLDR

Go async at the top level by encapsulating your code within an Immediately-Invoked Function Expression (IIFE). This enables you to use await right out the gate:

(async function() { console.log(await fetchData()); // Ain't no one got time to wait! })();

This pattern delivers a nifty way to use await at the top level where native support is missing, without reworking your whole code structure.

Making async/await work at the top level

With the power of ECMAScript modules and certain JavaScript environments, top-level await bursts onto the scene, streamlining your async code:

const data = await fetchData(); // "I'll wait for you, fetchData <3" console.log(data);

This nifty feature can be accessed in Node.js v13.3+ with the --harmony-top-level-await flag or from v14.8+ natively. Additionally, it is supported by TypeScript (v3.8+), Deno, and certain Webpack configurations.

Taming the error beast

Manage errors when using await at the top level using a try/catch block:

try { const data = await fetchData(); // Go get 'em, tiger! console.log(data); } catch (error) { console.error('Failed to fetch the goodies:', error); // Well, that escalated quickly... }

Alternatively, a .catch() appended to promises tidies up unhandled rejections:

fetchData().catch(error => console.error('Got a boo-boo:', error)); // Error handlers to the rescue!

Awakening compatibility with Node.js

To enable ESM syntax, including top-level await, in a Node.js project which relies on CommonJS, you can convert files to the .mjs format or specify "type": "module" in your package.json:

// In package.json { "type": "module" }

Supercharging asynchronous execution

Prep your functions for easier use with await using Node.js's fabulous promisify utility. Your code readability will thank you for it:

const { promisify } = require('util'); const setTimeoutPromise = promisify(setTimeout); (async () => { console.log('Wait for it...'); // Not THAT long... await setTimeoutPromise(2000); console.log('This just in...'); // Asynchrously refreshing! })();

Fetching HTTP requests with axios, in style

When getting data from an API, axios pairs perfectly with top-level await, improving readability:

const axios = require('axios'); (async () => { const response = await axios.get('https://api.example.com/data'); // "Where's my data at?" console.log(response.data); // "Here it is!" })();

Using top-level await, you can say goodbye to chaining .then().

When scripts join forces

Concatenated Node.js scripts may lose their async nature. To handle this, consider creating a new async scope:

// Concatenated scripts can be made async-ready using an IIFE (async () => { // Your combined script's async code... })();

Pros & cons of top-level await

While top-level await makes async programming easier, be aware of the following trade-offs:

  • Pros:

    • Code Readability: Asynchronous code that looks synchronous for the win!
    • Efficient Loading: Dependencies follow the best sequence without extra code.
    • Simpler Error Handling: Try/catch blocks can directly surround awaited calls.
  • Cons:

    • Potential Performance Costs: Misuse could lead to unintentional stops in async flow.
    • Import Caution: All importing modules will wait, possibly affecting startup time.
    • Older Compatibility: Top-level await might not be available in older environments or toolchains.

Overcoming challenges

While implementing async/await is beneficial, it presents some hurdles:

  • Concatenated Files: Create a new async scope for your concatenated scripts.
  • CommonJS patterns in Node.js: Refactor your code using esrun for TypeScript or use .mjs format while keeping traditional Node.js style.

Enhancements and final words

Couple more tips before you brave the async/await terrain:

  • Debugging with Chrome DevTools, Node.js, or Safari Web Inspector supports top-level await.
  • For a different flavor of async programming, try the object-oriented promise API.
  • Always check compatibility of your specific environment before employing top-level await.