From what I understand, async/await enables to avoid the "callback pyramid of doom".
Let's say that I have funcA(), funcB() and funcC() that are async functions, now if I do something like that:

func someFunction() -> T {
let myValueA = await funcA()
let myValueB = await funcB()
let myValueC = await funcC()
return doSomething(myValueA, myValueB, myValueC)

Why do some languages (for instance JavaScript) require you to annotate your function with async ?
What would be the issue issue if the function wasn't asynchronous? I mean, since the function already contains awaits, can't it be already considered as a non blocking function ?

Thank you

  • $\begingroup$ No, a blocking function can easily call asynchronous functions and wait until they deliver their results. Absolutely a bad idea, but absolutely possible. $\endgroup$
    – gnasher729
    Aug 20 '17 at 17:40
  • $\begingroup$ @gnasher729 Doesn't await forces the instruction to wait until the called function returns ? $\endgroup$ Aug 20 '17 at 17:42
  • $\begingroup$ Why are you asking us, and not the designers of those languages? $\endgroup$
    – Raphael
    Aug 20 '17 at 18:22
  • $\begingroup$ @Raphael Well, isn't that still a CS question? $\endgroup$ Aug 20 '17 at 18:23
  • $\begingroup$ Different languages don't necessarily handle async await the same way $\endgroup$ Aug 20 '17 at 18:23

The async annotation changes the way the body of the function is interpreted. Now you can clearly infer that if the body contains await, it must be async, but there are a couple of reasons why it makes sense to require an annotation.

The primary reason is, frankly, likely clarity. C# popularized this syntax and is a language that emphasizes declaring intent. My understanding (from vague recollections of blog posts) is that the C# designers view the fact that they didn't do a similar thing for generator syntax (i.e. yield) as a mistake or at least unpleasant compromise. Note that in JavaScript you do need to indicate a function is a "generator function" in much the same way as async. At any rate, both async/await and yield lead to dramatically different semantics, and you probably want to be clear that that is intended and understood. (A minor factor for C# was to make the await keyword contextual to avoid backwards compatibility issues.)

As a more technical reason, async and JavaScript's function* change the behavior of a function even if you don't use await/yield. In C#, an async method with no await will run synchronously, but it will still package up the result in a Task object. Similarly, in JavaScript, a generator function with no yield still returns an empty Iterable rather than undefined. If the async/function* aspect was inferred from usage of await/yield, then in these cases you would need to explicitly create a Task or Iterable. This isn't so bad in C# where the type system will require this, but in JavaScript there's a much greater chance for uncaught errors. Regardless, it's also bad for exploration since commenting out a line of code might dramatically change the behavior and type of the function.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.