Why is async required to call await inside a JavaScript function body?

I assume your exact question is this: "Handling return values (null or something) depends on the consumer who called the function. They "should" supposedly get it even if another asynchronous function is called in-between. So why does it matter to wait before further other asynchronous calls?"

You see, such fetch() calls are done in Databases within the duration of "initiating" and "closing" the connection. Almost every method used is asynchronous in this case. So while you're executing fetchMovies(); The thread might move further and execute connection.close(); before the fetching is resolved and returned.

The exact scenarios are like this:

await client.connect();  //connection establishment
// Your purposeful methods
async function fetchMovies() {
  const response = await fetch('/movies');
  console.log(response);
}

await fetchMovies();

// Closing connection to avoid attacks and maintain concurrency
await client.close();

If any method, in this case, is called in an asynchronous manner, the whole session of a Database connection is wasted and our function would return undefined or throw an error "Cannot use a session that has ended"

So we need to "wait" for the "Pending" Promises to reach a "Fulfilled" or "Rejected" state before executing further calls.

You can refer more to this article: Using Promises, async / await with MongoDB just for the sake of understanding.


There are three reasons the async keyword exists:

  1. In ECMAScript language versions prior to 2015, await was not a keyword. Marking a function async provides a syntactic "bailout" to indicate a breaking change in the language grammar within the body of the function.

    This is the most important reason. Without the async keyword, all programs written in ECMAScript 5 or older would no longer work if they used the await keyword as a variable (in fact this was done intentionally in some cases as a polyfill before async/await was standardized), since that would cause a breaking change without the addition of async to the specification. Because of this, async is syntactically necessary to avoid breaking changes to the language.

  2. It provides a convenient marker for parsers, avoiding an infinite look-ahead in order to determine whether or not a function is asynchronous.

    This makes parsing more efficient, which is appealing for both ECMAScript implementers and developers, though this reason alone does not make async strictly necessary to the syntax.

  3. async also performs its own transformation on the function, which is done regardless of whether or not the await keyword is present in the body.

    Consider the following two functions:

    function foo() {
      if (Math.random() < 0.5) {
        return 'return';
      } else {
        throw 'throw';
      }
    }
    
    async function bar() {
      if (Math.random() < 0.5) {
        return 'return';
      } else {
        throw 'throw';
      }
    }
    

    async performs the following transformation of function bar():

    function bar() {
      return new Promise((resolve, reject) => {
        try {
          resolve((/*async function bar*/() => {
            if (Math.random() < 0.5) {
              return 'return';
            } else {
              throw 'throw';
            }
          })());
        } catch (reason) {
          reject(reason);
        }
      });
    }
    

    Those familiar with promises will recognize that we can simplify the above since the Promise constructor executor function will implicitly reject if it throws an error synchronously:

    function bar() {
      return new Promise((resolve) => {
        if (Math.random() < 0.5) {
          return resolve('return');
        } else {
          throw 'throw';
        }
      });
    }