15
votes

Why does using await need its outer function to be declared async?

For example, why does this mongoose statement need the function it's in to return a promise?

async function middleware(hostname, done) {
  try {
    let team = await Teams.findOne({ hostnames: hostname.toLowerCase() }).exec();
    done(null, team);
  } catch (err) { done(err); }
}

I see the runtime/transpiler resolving the Teams promise to it's value and async signaling it "throws" rejected promises.

But try/catch "catches" those rejected promises, so why are async and await so tightly coupled?

3
I don't get what you mean by "But try/catch "catches" those rejected promises". How is that relevant for the async keyword?Bergi
"why does this mongoose statement need the function it's in to return a promise?" - how else would it be able to wait for the asynchronous result of the mongoose promise?Bergi
Here's a reason for the language design: stackoverflow.com/a/41744179/1483977Michael Cole

3 Answers

8
votes

I'm not privy to the JavaScript language design discussions, but I assume it's for the same reasons that the C# language requires async (also see my blog).

Namely:

  1. Backwards compatibility. If await was suddenly a new keyword everywhere, then any existing code using await as a variable name would break. Since await is a contextual keyword (activated by async), only code that intends to use await as a keyword will have await be a keyword.
  2. Easier to parse. async makes asynchronous code easier to parse for transpilers, browsers, tools, and humans.
6
votes

Copied from https://stackoverflow.com/a/41744179/1483977 by @phaux:

These answers all give valid arguments for why the async keyword is a good thing, but none of them actually mentions the real reason why it had to be added to the spec.

The reason is that this was a valid JS pre-ES7

function await(x) {
  return 'awaiting ' + x
}

function foo() {
  return(await(42))
}

According to your logic, would foo() return Promise{42} or "awaiting 42"? (returning a Promise would break backward compatibility)

So the answer is: await is a regular identifier and it's only treated as a keyword inside async functions, so they have to be marked in some way.

Fun fact: the original spec proposed more lightweight function^ foo() {} for async syntax.

1
votes

Because using await inside middleware function means the middleware function can't return a result immediately (it must wait until await is settled) and middleware function callers must wait until the promise (returned from middleware function) is settled.