admin管理员组

文章数量:1279083

I have a short Node.js script where I require another package and call an async function from it and subsequently want to print the return value. If I simply await the return value from the top level, then I'll get an error, saying that I can only use await inside an async function itself. So apparently the way to go is like this:

async function main() {
  foo = await someOtherAsyncFunc();
  console.log(foo);
}
main()

Or:

(async function() {
  foo = await someOtherAsyncFunc();
  console.log(foo);
})();

Or:

(async () => {
  foo = await someOtherAsyncFunc();
  console.log(foo);
})();

(Credit to VLAZ in chat )

This works - but I want to understand the reasons behind it a little bit more: I'm used to not being able to directly use await from the top level. However, I'm also used to having to call some special library function to actually "venture" into async from the top level. In Python, see asyncio.run for example. What's the point of requiring await to be inside an async function - if I can then call just any async function from the top level? Why then isn't await available at top level, too?

I have a short Node.js script where I require another package and call an async function from it and subsequently want to print the return value. If I simply await the return value from the top level, then I'll get an error, saying that I can only use await inside an async function itself. So apparently the way to go is like this:

async function main() {
  foo = await someOtherAsyncFunc();
  console.log(foo);
}
main()

Or:

(async function() {
  foo = await someOtherAsyncFunc();
  console.log(foo);
})();

Or:

(async () => {
  foo = await someOtherAsyncFunc();
  console.log(foo);
})();

(Credit to VLAZ in chat https://chat.stackoverflow./transcript/message/54186176#54186176)

This works - but I want to understand the reasons behind it a little bit more: I'm used to not being able to directly use await from the top level. However, I'm also used to having to call some special library function to actually "venture" into async from the top level. In Python, see asyncio.run for example. What's the point of requiring await to be inside an async function - if I can then call just any async function from the top level? Why then isn't await available at top level, too?

Share Improve this question asked Mar 17, 2022 at 18:53 finefootfinefoot 11.3k12 gold badges75 silver badges121 bronze badges 5
  • 2 v8.dev/features/top-level-await – Patrick Roberts Commented Mar 17, 2022 at 18:58
  • 1 stackoverflow./questions/39679505/… – James Commented Mar 17, 2022 at 18:58
  • An async function is simply a function that returns a Promise. The await keyword is there to introduce, basically, a .then() call around the rest of the code in the function. Thus, when you call an async function but don't await it, that's fine, because eventually the Promise will resolve (or reject) and the rest of the code in the function will run. – Pointy Commented Mar 17, 2022 at 19:08
  • I think the answer may be related to the fact that top-level await used to not be a thing is that there weren't any natural approaches for downstream scripts to consume results from a script that uses top-level await. It doesn't make much sense to try to export something after an await, for example. – CertainPerformance Commented Mar 17, 2022 at 19:11
  • Does this answer your question? Using await outside of an async function – 0Valt Commented Mar 17, 2022 at 20:12
Add a ment  | 

1 Answer 1

Reset to default 9

Top-level await used to not be a thing, but it is possible now in ES6 modules.

One reason why top-level await used to not be a thing, and is still not a thing outside of modules is that it could permit syntactical ambiguity. Async and await are valid variable names. outside of modules. If a non-module script permitted top-level await, then, short of re-working the specification (and breaking backwards patibility), there would be circumstances when the parser couldn't determine whether a particular instance of await was a variable name, or was used as the syntax to wait for the Promise on its right-hand side to resolve.

To avoid any possibility of ambiguity, the parser, when parsing a section of code, essentially needs to have flags that indicate whether await is valid as an identifier at any given point, or whether it's valid as async syntax, and those two must never intersect.

Module scrips permit top-level await (now) because the use of await as an identifier has always been forbidden in them, so there is no syntactical ambiguity.

In contrast, there are zero issues with using .then on the top level because it doesn't result in any ambiguity in any circumstances.

Why doesn't it just return a Promise which is never executed because it doesn't get awaited?

Promises aren't really "executed". They can be constructed, or waited on to fulfill, or waited on to reject. If you have a Promise, you already have some ongoing code that will (probably) eventually result in a fulfillment value being assigned to the Promise.

Hanging Promises are syntactically permitted - values that resolve to Promises but which aren't interacted with elsewhere. (Which makes sense - every .then or .catch produces a new Promise. If every Promise had to be used by something else, you'd end up with an infinite regress.)

Doing

(async () => {
  foo = await someOtherAsyncFunc();
  console.log(foo);
})();

is essentially syntax sugar for

someOtherAsyncFunc()
  .then((foo) => {
    console.log(foo);
  });

There's no need to tack anything else onto the end of either of those. (though it's remended to add a .catch to a dangling Promise so unhandled rejections don't occur)

本文标签: