Originally published at: http://www.sitepoint.com/simplifying-asynchronous-coding-es7-async-functions/
The debut of Promises in JavaScript has lit the internet on fire—they help developers break out of callback hell and solve a lot of problems that have plagued the asynchronous code of JavaScript programmers everywhere. Promises are far from flawless, though. They still require callbacks, can still be messy in complex situations, and are incredibly verbose.
With the advent of ES6, which not only makes promises native to the language without requiring one of the countless available libraries, we also get generators. Generators have the ability to pause execution within a function, which means that by wrapping them in a utility function, we have the ability to wait for an asynchronous operation to finish before moving on to the next line of code. Suddenly your asynchronous code can start to look synchronous!
But this is just the first step. In ES7, async functions will be released. Async functions take the idea of using generators for asynchronous programming and give them their own simple and semantic syntax. Consequently, you don’t have to use a library to get that wrapping utility function, because that is handled in the background.
Traceur is installed through npm:
npm install --global traceur
And then used on a source file like so:
traceur --experimental --out file1.js file2.js
The experimental flag is necessary for async functions and generators.
You’ll also need to include the Traceur runtime in your HTML. The runtime comes as part of the Node module and can be found in its bin directory.
Async Functions vs Generators
Here is an example of using generators for asynchronous programming. It uses the Q library:
var doAsyncOp = Q.async(function* () { var val = yield asynchronousOperation(); console.log(val); return val; });
Q.async
is the wrapper function that handles everything behind the scenes. The *
is what denotes the function as a generator function and yield
is how you pause the function and let the wrapper function take over. Q.async
will return a function that you can assign—as I have done—to doAsyncOp
and subsequently invoke.
Here’s what it looks like when you get rid of the cruft by using the new syntax included in ES7:
async function doAsyncOp () { var val = await asynchronousOperation(); console.log(val); return val; };
It’s not a lot different, but we removed the wrapper function and the asterisk and replaced them with the async
keyword. The yield
keyword was also replaced by await
. These two examples will do the exactly same thing: wait for asynchronousOperation
to complete before assigning its value to val
, logging it, and returning it.
Converting Promises to Async Functions
What would the previous example look like if we were using vanilla promises?
function doAsyncOp () { return asynchronousOperation().then(function(val) { console.log(val); return val; }); };
This has the same number of lines, but there is plenty of extra code due to then
and the callback function passed to it. The other nuisance is the duplication of the return
keyword. This has always been something that bugged me, because it makes it difficult to figure out exactly what is being returned from a function that uses promises.
As you can see, this function returns a promise that will fulfill to the value of val
. And guess what … so do the generator and async function examples! Whenever you return a value from one of those functions, you are actually implicitly returning a promise that resolves to that value. If you don’t return anything at all, you are implicitly returning a promise that resolves to undefined
.