I've intentionally been keeping myself ignorant about async/await in JS until it's common enough I can consider using it natively without any great concern about cross browser compatibility. Why is everyone so excited for it? From the example it looks like a thin syntactic sugar layer over normal promises, what's so great about saving one level of indentation?
Well, I guess there are three main reasons I'm so excited about async/await:
1. It allows you to reason linearly about code with IO mixed in, without blocking the entire event loop. This is incredibly useful. Computer programmers are very good at reasoning linearly. It is just much, much more comfortable [1] to think about code that uses async/await than the same code with promises.
2. I think the way that promises and async/await interop is very beautifully designed. The way it ends up working in practice is that you can write a function that "blocks"[2], but if you later decide you want to call it in parallel with something else, that's fine because async functions are just a thin wrapper around Promises. So you can just take that existing function and call `Promise.all()` on it. Similarly if you start with a function that returns a promise, and you decide you want to call it in a blocking way -- you just do `await thingThatReturnsPromise()`. This is an almost perfect example of primitives composing to be more than the sum of their parts.
3. (and this is worth the price of admission alone) error handling works properly and automatically. If you `await` an async function and it throws, you'll get a plain ol' JavaScript error thrown which you catch in the normal way. If I never debug another hung JavaScript app that dropped a rejected promise on the floor it will be too soon.
[1] please don't tell me that I just don't "get" asynchronous programming. I was writing js event handlers when you were in diapers (something something lawn something).
[2] `await` doesn't really block the event loop, it just appears to.
One more thing that's particularly nice about `async/await` is just using the `async` part. For existing functions that should always return promises, when that function is `async` you can be sure of it. Even if it throws. Even if it returns a value that sometimes isn't a promise.
I'm not up to speed on async/await -- do you happen to know if an async function throws on synchronous code (before the first await keyword) -- does that result in an asynchronously rejected promise or a synchronous exception?
This added contract between the caller and callee is huge. No more searching for that one branch of a non-trivial function that failed to wrap its immediately available return value in a Promise.
> [1] please don't tell me that I just don't "get" asynchronous programming. I was writing js event handlers when you were in diapers (something something lawn something).
This sounds pretentious and detracts from an otherwise good comment, even if you didn’t mean it to.
The main problem with normal promises is you can no longer use native control structures, because the code before and after the operation has to go into separate functions. Loops no longer look like loops, the bodies of conditionals are less obvious, exception catch points are hidden, etc.
With async/await the language takes care of lowering those control structures that straddle async operations, similarly a compiler lowering them into conditional branch instructions.
I've used both. In my opinion, promises are fine for simple scenarios, but once you start doing anything complex, the synchronous "feel" of async/await make your code much easier to read and reason about.
Ability to use asynchronous functions without pulling everything depending on them into a closure, which comes with a lot of baggage in how you have to structure things.
Depends how you look at it. The straightforward answer would be "no" because await lets you assign the result of a promise to a variable without calling `.then(...)` with a callback and then accessing the variable inside the callback (where the callback would be the closure). In that way of thinking, async/await directly let us write code with less closures.
Note that that explanation was purposefully ignoring any implementation details about whether or not closures are actually being created/allocated and was purely focusing on whether the programmer had to type out/read closures as extra syntax.
Actually, functional programmers might point out that even without async/await, assignment in imperative languages is still just some syntactic sugar for actually using closures. That is, in functional languages, its almost never idiomatic to directly "assign" to a variable (that is, overwriting the variable's previous value); instead values are "bound" to variables in let statements (or whatever the language's idiomatic equivalent is)--and let statements are effectively syntactic sugar for function calls/closures.
If you're familiar with haskell: you can think of imperative code as haskell's do notation but with the Identity macro (aka no special mode of computation, just the results of each function flowing into the next with "assignment" converted into functions). Marking a function as async (and thus allowing you use to await inside it) is just switching that function to be under the Promise monad. (but be careful! JavaScript promises aren't strictly monadic in that they auto-resolve if nested... so you can't have "Promise Promise a")
(... Sorry, this ended up being a bit of a mess of a braindump because you specified "implicit" closures :) )
async/await is well designed in that it "converts" into a Promise (and you can await Promises). Even though async code is still "contagious" if you need the result of the computation, sync and async code can now be mixed – the sync code can pass the promises around. This makes it a lot more convenient to use i.e. code is more concise.
Just as a point of interest (maybe), I've been using Redux-Saga on a project it essentially affords the same benefit; it abstracts over any of the JS async primitives (callbacks, promises, generators, etc). It is awesome!
If you have a linear chain of async operations (do this, then this, then this, ...) it doesn't change a lot compared to raw promises.
However if you have control flow in it (execute this async function 100 times, add the results, depending on the result call another async function, ...) things will get a lot more ugly with raw promises.