2

In the latest version of languages like TypeScript or ECMAScript you can use async/await constructs to write code that combines the clean structure of synchronous programming with the performance advantages of asynchronous code.

Take this as an example:

async function isAdmin() {
    // Async IO request...
    return false;
}

async function doSomething() {
    if (await isAdmin()) {
        console.log("Done");
    } else throw new Error("Unauthorized");
}

doSomething();

It looks very clean. However, because of the synchronous look of the code it's not that difficult to forget to await on some function invocation, thus writing things like this:

// ...
if (isAdmin()) {
    console.log("Done");
} else throw new Error("Unauthorized");

which is dangerously wrong.

What's the rationale behind this choice, instead of awaiting all async functions by default and letting the programmer choose which operation to do asynchronously? Something like this made-up syntax:

var admin = isAdmin(); // wait isAdmin to return a result
async doSomething();   // call doSomething asynchronously
doSomething();         // call doSomething synchronously
var promise = async doSomething(); // Get the underlying Promise
7
  • 1
    Related: Why do we need the async keyword? Commented Aug 10, 2016 at 15:49
  • 6
    What makes your example dangerously wrong is javascript's weak typing that allows you to treat a promise like a boolean. Commented Aug 10, 2016 at 16:09
  • 1
    @CodesInChaos: It works similarly in C# as well. A promise is returned, but because it has await in front of it, you can treat it as if it is returning the actual promised value. Commented Aug 10, 2016 at 16:19
  • @CodesInChaos True, but even with stronger typing we would get the same issue by handling exceptions of async methods. Btw, thanks for your answer, clear and right to the point :)
    – danieleds
    Commented Aug 10, 2016 at 16:19
  • 1
    These answers are surprising; I thought it was entirely for historical reasons, to maintain backward compatibility, not because anybody thought it was a good design. Commented Mar 3, 2018 at 5:29

2 Answers 2

2

Note that a function being async doesn't turn it into something completely different, it just enables some syntax. A normal function returning a promise is just as asynchronous as a function marked with async.

  1. The await serves as a warning sign "the world may have changed in the mean time".

    await may be easier than multi threading, but it's still very error prone. During code review you have to look at each await, thinking "does the function rely on any information obtained before the await still being the same? Is this guaranteed?". Without an explicit await, you have to do so at every function invocation.

  2. Backwards compatibility. With your proposal code behaves completely different if the browse/runtime supports awaiting or not.
  3. If your code doesn't want to deal with awaiting, it needs to insert an async at every call to an externally defined function, just in case it returns a promise.
  4. Performance. The runtime needs to insert an if result is a promise then await check at every method call.
1

Because you may not want to await all of your functions. You might want some of them to run synchronously.

Your example is not "dangerously wrong," it's merely synchronous.

By the way, you shouldn't have to await a function named isAdmin(); it should return almost immediately anyway, and since the if statement needs the return result before it can proceed, it's probably not even going to run asynchronously.

As to why two keywords are used instead of one, it's explained in the TypeScript proposal for Async Dunctions:

An Async Function is a JavaScript Function, Parameterized Arrow Function, Method, or Get Accessor that has been prefixed with the async modifier. This modifier informs the compiler that function body transposition is required, and that the keyword await should be treated as a unary expression instead of an identifier.

8
  • The isAdmin function was an example; suppose it needs to check the database. In addition, I think you missed the last example in my question, where I show a potential alternative way to run function asynchronously :)
    – danieleds
    Commented Aug 10, 2016 at 15:38
  • 1
    Well, the async await model originates from C#. In C#, the async keyword signals the compiler that the method so marked has async semantics. But you still have to await a method within that async method to make it asynchronous, otherwise the async keyword is a big, fat no-op. Without the async keyword you'll get a compiler error when you try to await. Commented Aug 10, 2016 at 15:44
  • The reasons why C# did it this way are described here. I assume TypeScript did it the same way because C# and TypeScript are both Microsoft products. Commented Aug 10, 2016 at 15:52
  • I think you're missing the point of my question. What I'm asking is: why do I have to explicitly await asynchronous functions, instead of implicitly await them and explicitly call them asynchronously?
    – danieleds
    Commented Aug 10, 2016 at 16:32
  • How is the method going to know it is asynchronous if you don't put await in front of it? Commented Aug 10, 2016 at 16:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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