It has been postulated (with sound reasoning) that top level await is a bad idea.

If so, are there any advantages to having await at all, when we already have yield.

Having async is useful because an async function is guaranteed to return a promise irrespective of what is actually returned by the code. But do have some similar reasoning behind await as well ?

Write your answer…

When @sandeep calls, I follow ;-)

Rich Harris's Gist-Blog post drew some attention :-)

What is async and await?

async/await is syntactical sugar on top of generator functions which yield Promises. Personally, I don't like the generator syntax; that's why I sparingly write any code like this function* fn() { yield maybe.something() }.

So I think async and await are excellent additions but as Rich Harris pointed out, the parser and code generators must do much better or only restrict its usage or use cases.

The more I think about it, the less I need async and await. Most of my problems are gone when we have widely accessible cancellable Promises. That would solve more of my code smell. Because who expects a Promise when one defines an async function() { which await maybe.something() }. I opened more questions then I answered, sorry ;-)


The more I think about it, the less I need async and await. Most of my problems are gone when we have widely accessible cancellable Promises.

The primary reason I use control flow constructs (yield or async/await) is unified error handling through try-catch.

Reply to this…

Hashnode is building a friendly and inclusive dev community. Come jump on the bandwagon!

  • 💬 A beginner friendly place

  • 🧠 Stay in the loop and grow your knowledge

  • 🍕 >500K developers share programming wisdom here

  • ❤️ Support the growing dev community!

Register ( 500k+ developers strong 👊)

So after some introspection and exploration, the answer is somewhat obvious.

When using generators it is the application developer's responsibility to pass the generator to the runner (eg. co). Async/await can be thought of as a higher level abstraction than can build upon this to make this implicit and remove the responsibility from application developer's hands.

Whenever we await inside an async function, the runtime ensures that we either get back a resolved value or fail with an exception.

Plus, similar to async function always return a promise irrespectively what the code returns, await can handle things that are not promises (by effectively calling Promise.resolve).

This makes async await more obvious choice for asynchronous control flow and generators for their intended use case - building iterators. This intent is also supported by the upcoming TC39 async-iteration proposal (with a regenerator polyfill) that further extends these ideas to support asynchronous iterators:

async function* iterator(arg) { .... }

Async generator functions are similar to generator functions, with the following differences:

  • When called, async generator functions return an object, an async generator whose methods (next, throw, and return) return promises for { next, done }, instead of directly returning { next, done }. This automatically makes the returned async generator objects async iterators.
  • await expressions and for-await-of statements are allowed.
  • The behavior of yield* is modified to support delegation to async iterables.
async function* readLines(path) {
  let file = await fileOpen(path);

  try {
    while (!file.EOF) {
      yield await file.readLine();
  } finally {
    await file.close();

This makes the usage scenarios explicit and makes interoperability seamless.