About "Await is only valid in Async functions" error in JavaScript
Tags: javascript, programming, webdev
Published: 2023-02-04 16:31:22

> ✋ **Update:** This post was originally published on my blog [decodingweb.dev](https://www.decodingweb.dev), where you can read the [latest version](https://www.decodingweb.dev/javascript-await-is-only-valid-in-async-functions) for a 💯 user experience. _~reza_

Await is only valid in Async functions; This syntax error occurs when you use an `await` expression outside an async execution context, like an async function or top-level body of an ES module (top-level await).

Here’s what it looks like:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kw1yy8sapclwrti1wd3c.jpg)


## How to solve “await is only valid in async functions” error?

Await expressions are used in two ways:

1. <u>Using await expressions in async functions</u>
2. <u>top-level await</u>

**Using await expressions in async functions:** If you’re using an `await` expression in a function, adding the `async` keyword to the function declaration resolves the issue.

```javascript
// ⛔ Wrong
function getBooks() {
    let books = await fetch('some-url/api/v1/books')
}

// ✅ Correct
async function getBooks() {
    let books = await fetch('some-url/api/v1/books')
}
```

Just beware that once you make a function `async`, it'll always return a promise. So if you're using the respective function in other places, remember to update your code accordingly.

You can also make callback functions async:

```javascript
setTimeout(async () => {
    const items = await getItems()
}, 2000)
```

**Top-level await:** If your `await` statement isn't in a function, you can wrap your code in an async IIFE (Immediately Invoked Function Expression):

```javascript
(async () => {
   let books = await fetch('some-url/api/v1/books')
   // Any code here will be executed after the books variable has the value.
})()
```

Await can be used on its own in ES modules too. If you're using Node js, you must set Node's module system to [ES module system](https://www.decodingweb.dev/fix-node-cannot-use-import-outside-module) first; Top-level await isn't supported by the Node.js default module system (CommonJS).

To do that, add `"type": "module"` to your `package.json` file. If you don't have a `package.json` file yet, run the following terminal command from your project directory:

```
npm init
```

Then add `"type": "module"` to your module's configuration:

```
{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "type": "module",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC"
} 
```

If your module is supposed to be loaded in a browser, add `"type=module"` to the `<script>` tag, and you're good to go:


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

If you're curious how async/await works, please read on.

## Understanding async functions in JavaScript

Async functions are easy to spot as they have the `async` keyword in their declaration. 

```javascript
async function myFunction () {
  // await can be used here ...
}
```

An async function always returns its value wrapped in a Promise (if the returned value isn't already a promise). You can access the returned value once it's resolved (if not rejected). 

Let's see an example:

```javascript
async function asyncTest() {
    return 1
}

let asyncFunctionValue = asyncTest()
console.log(asyncFunctionValue)
// output: Promise { 1 }

// Get the value when it's resolved
asyncFunctionValue
  .then(value => {
    console.log(value)
   // output: 1
  })
```

So basically, the `async` keyword implicitly wraps the returned value in a promise (if it's not already a promise).

The above code is equivalent to the following:

```javascript
function asyncTest() {
  let returnValue = 'someValue' 
  return new Promise.resolve(returnValue)
}
```

Now, what's the `await` keyword?

The async/await duo enable you to write asynchronous code more cleanly by avoiding promise chains (a cascade of `then()` methods).

```javascript
promiseObj
.then(value => {
// some code here
})
    .then(value => {
    // some code here
    })
        .then (value => {
        // some code here
        })
            .then(value => {
            // some code here
            })
```

The `await` keyword makes JavaScript look synchronous, even though it never blocks the main thread. The purpose of using `await` inside an async function is to write cleaner asynchronous code in promise-based APIs, like the Fetch API.

The rule is, await expressions must be used inside async functions. Otherwise, you'll get the syntax error "await is only valid in async functions and the top level bodies of modules".

Let's make it clear with an example. 

Using the fetch API in the old-fashioned way is like this:

```javascript
fetch('some-url/api/v1/movies')
  .then(response => response.json())
  .then(data => console.log(data))
```

But with async/await, you won't need `then()` callbacks:

```javascript
let response
(async () =>  {
     let movies = await fetch('some-url/api/v1/movies')
     // The code following await is treated as if they are in a then() callback
     response = await movies.json()
})()
```

So when JavaScript encounters an `await` expression in your async function, it pauses the execution of the code following `await` and gets back to the caller that invoked the async function. The code following await is pushed to a microtask queue to be executed once the promise being awaited is resolved.

The following code is a simplified (and imaginary) chatbot that starts a chat session with a user. We have a function named `say()`, which returns messages after a delay (to mimic human typing).

```javascript
function say(text, delay = 500) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(text)
        }, delay)
    })
}

async function startConversation() {
    console.log('Hi!')   
    console.log(await say('my name is R2B2,'))
    console.log(await say('How can I help you?'))
}

startConversation()
console.log('Please input your email:')
```

However, the function doesn't return the messages in the order we expect:

```
Hi!
Please input your email:
my name is R2B2,
How can I help you?
```

The reason is once JavaScript gets to the first `await`, it pauses the execution of what's left in the function and returns to the caller (`startConveration()`). The main thread that is now freed, prints the `"Please input your email"` message.

And once the promises are resolved, the function's remaining lines are executed - as if they were inside a callback function.

It's just the good old `then()` callback functionality but more cleanly!

Additionally, the async/await duo lets us use try/catch with promised-based APIs. Something you couldn't simply do with `then()` callbacks.

```javascript
let items = []
try {
    items = await getItemsFromApi()
} catch (error) {
   // Handle the error here
}
```

## A quick note on performance

Since the code after the `await` is to be paused execution, you gotta make sure it's only followed by the code that depends on it. Otherwise, some operations will have to wait for no reason.

Imagine we need to get the best deals from Amazon and Etsy and merge the results into an array (to be listed on a web page).

The following approach isn't optimized:

```javascript
function getAmazonDeals() {
    // get Amazon deals ...
}

function getEtsyDeals() {
    // get Etsy deals ...
}


// Using an IEEF function here ...
(async () => {
    const amazonDeals = await getAmazonDeals(1000)
    const etsyDeals = await  getEtsyDeals(1000)

    const allDeals = [...amazonDeals, ...etsyDeals]
    populateDealsList(allDeals)

})()
```

In the above example, the lines following the first `await` are paused until the data is fetched from Amazon. This means the second request (`getEtsyDeals()`) has to wait without being dependent on the return value of `getAmazonDeals()`.

So if each request takes one second, fetching deals from Amazon and Etsy would take two seconds in total.

But what if we initiated both requests concurrently and use await afterward?

Let's see how:

```javascript
function getAmazonDeals() {
    // get Amazon deals ...
}

function getEtsyDeals() {
    // get Etsy deals ...
}


// Using an IEEF function here ...
(async () => {
    // We're not using await here to initiate the requests immediately
    const amazonDeals = getAmazonDeals(1000)
    const etsyDeals = getEtsyDeals(1000)

     // Since the result of both requests are still promises, we use await when we want to combine them into one array
    // The leads aren't populated until we have deals from both sources
    const allDeals = [...await amazonDeals, ...await etsyDeals]
    populateDealsList(allDeals)
})()
```

Since both requests start immediately, we have both responses in one second.

 I hope you found this quick guide helpful.

Thanks for reading.