Simplifying Asynchronous Coding with ES7 Async Functions

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.

Continue reading this article on SitePoint

I think that more better approach is https://babeljs.io. This tool transform to a more readable javascript and support ES7 and ES6 features.

I think Babel is a great tool as well. I wrote this article months ago and it finally came through the pipeline. I have come to learn of 6to5/Babel since then and I like it. I love its CLI’s better than Traceur’s one CLI that tries to do everything and just ends up convoluting the arguments you can pass in. Also, Babel now has better support for features than Traceur, though when I first read about it, it was lagging slightly.

The argument about more readable ES5 has never made much sense to me, though. How often do you actually read the generated ES5? We have source maps so that we don’t have to worry about that. The one thing I would worry about regarding the generated code is the KB size, which can go either way depending on the features you use and the options you use to compile them. Otherwise, as long as the code works, just worry about how your ES6 code looks.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.