ECMAScript version 5 is the latest complete version of JavaScript available and is implemented in every major browser, but ES6 is in the works and promises a much better world for JavaScript developers with features such as a simple class syntax, arrow functions, built-in promises and the like. It doesn’t stop there, though; we’re already looking ahead to ES7 while ES6 is still cooking in the oven. In particular, I’m extremely excited about asynchronous functions.
The Current Situation, ES6, and Beyond
Before we dive into the asynchronous functions of ES7, let’s build up by showing how we currently implement things, how it’ll change in ES6, and then show how asynchronous functions just make things even better. First off, we’re going to take a look at promises. Promises are a standard laid out in the Promises/A spec. Right now there are dozens of libraries that provide their own implementations of this spec, and most also throw some other features on top. It’s great that we have a standard, and the most important functionality is implemented the same across the board, but there are still a lot of inconsistencies between the libraries. It’d be nice if we could settle this down to a single implementation… we have! ES6 is bringing in its own implementation of promises which should take over and become the de facto way of doing things. I’m still on the fence about how I feel about the syntax, but that’s not a big issue.
Promises are great and everything, but we still like to write our code synchronously. Whether we’re using callbacks out the whazoo or replacing them with promises, it’s still harder to read than synchronous code. Well, another great ES6 feature has come to the rescue for us there: generators. Jmar777 talks about generators, giving us a quick rundown, and then goes on and tells us how he created a library that takes advantage of generators to force the code to simply wait until the asynchronous operation is finished before moving to the next line. This works really well and, for now, can be extremely useful.
Here’s an example (adapted from the async functions proposal page) of using pure promises vs using promises + Suspend (jmar777’s generator library):
1 | // With Pure Promises |
The magic here is in the suspend(function*()...
line and the yield
statement. I was blown away when I first saw that we could do this.
ES7’s Gift to the Web Developers
Using generators works, but it’s a bit of a hack. Generators weren’t originally designed for that, even if they serve that purpose well. Instead JavaScript will be receiving a built-in way to halt execution of code as we wait for an asynchronous operation to finish. We do this by using the await
keyword inside an async
function:
1 | // With Asynchronous Functions and `await` |
You must use async
on the function in order for await
to work. Also, notice that when you use await
, if the promise is resolved, it will evaluate to the value the promise was resolved with, so you can use a simple assignment like we did in the example. If the promise is rejected, it’ll throw an error, which means we can catch rejections with try
and catch
blocks. Using the await
keyword should work with any promise, not just ones returned from another asynchronous function or an ES6 built-in promise.
When we prepend a function
declaration with async
, it’ll return a promise without you having to even touch the promise API! To resolve the promise, just return a value from the function (or don’t return any values if you want it to resolve without a value), and if you want to reject the promise, just throw
your rejection value.
If you’re like me, you might be thinking that this is awesome, but it’s not really useful because this feature isn’t available yet. Well, that’s true, but the traceur compiler actually already supports compiling this feature to ES5, so if you think adding a build step is worth the time you’ll save, I’d definitely take a look into it.
Notes
You may have noticed that the asynchronous function example looks very similar to the Suspend example, except we don’t need to require a library for it, we don’t need the wrapping suspend
function, we add the async
keyword to the front of the function declaration, and we replace yield
with await
. There’s a reason for this similarity. To quote the spec page:
Async functions are a thin sugar over generators and a spawn function which converts generators into promise objects.
In other words, while I considered the use of generators as a hack, they are still being used behind the scenes; we’re just replacing them with a cleaner and clearer syntax specifically designed for promises. The reason why await
only works inside an async
function is because the async
keyword is the signal to the translator to replace it with a spawn
/suspend
function and swap all the await
keywords to yield
.
Also, keep in mind that this spec is in the very early stages, so things could change quite dramatically, though I can’t see what they’d change except maybe the keywords.
Finally, another great article about this is Jake Archibald’s article on ES7 async functions. It’s worth checking out.
Conclusion
I used to be excited about ES6, but now I’m more excited about ES7. Promises were supposed to be a solution to the asynchronous operation problem, but they really only solved a small subset of what we needed. I think asynchronous functions from ES7 take promises to the next level and really make asynchronous coding simpler. God Bless and Happy Coding!