Async-Await is an ES7 proposal, and one of the most exciting upcoming features of JavaScript. It is a huge improvement in code mantainability compared to the old callback and promises async invocation models available in ES5.

With Async-Await and Decorators in ES7, JavaScript will come even closer to "serious" and well-designed system languages like C#.

Async-Await certainly simplifies code, and makes it more readable and there is one area where async-await really shines, and that is handling conditional async execution, which is basically if-else in the async world.

Starting from sync code

One good approach in Async programming is writing the synchronous code first, and then converting it to asynchronous. So let's start wih a scenario where we run an HTTP request to get some JSON data, and then we conditionally parse the response if the body is valid, and save the data into a DB if the parsing succeeded.

try {  
    const response = requestSync(data);

    if (response && response.body) {
      const parsed = JSON.parse(response.body); 

      if (parsed) {
        const saved = saveDataSync(parsed);

        // Do something
      }
    }
} catch(err) {
    logger.error(err);
}

This code is intentionally simplistic, and it is just an example of sync code that we will use as a basis to compare promises vs async-await.

Conditional async code with promises

Let's start with promises and convert the code above to a flat promise chain that uses async API's instead of the sync API's, in a non-blocking fashion. It is a good idea to try to build your async promises code as a flat promise chain, which promotes vertical instead of horizontal expansion of continuations, and a clean separation of execution steps:

request(data)  
// Parsing step
.then(function(response) {  
    if (response && response.body) {
        return JSON.parse(response);
    }

    return null;
})
// Saving step
.then(function(parsed){
    if (parsed) {
        return saveData(parsed);
    }

    return null;
})
// Additional step (e.g. return/notify)
.then(function(saved) {
    if (saved) {
        // Return additional promises, keep it flat
    }
})
// Error handling step
.catch(function(error) {
    logger.error(err);
})
.done();

This code is fairly understandable, but you will notice that the code is plagued with if-else logic in each then() block to handle the conditional branching and different promise resolution scenarios. It is not very readable to have a then() block resolving to a null value in case the response body is not valid and could not be parsed.

Overall this code is arguably much more verbose than the sync counterpart, and it is harder to mantain.

The code above could be "simplified" by embedding all the async code in one then() block:

request(data)  
.then(function(response) {  
    if (response && response.body) {
        const parsed = JSON.parse(response);

        if (parsed) {
           saveData(parsed).then(function(saved) {
             if (saved) {
                // Do additional .then() branching inwards. Grow the pyramid!
             }
           })
           .catch(function(err) {
             logger.error(err);
           })
           .done();
        }
    }

    return null;
})
.catch(function(error) {
    logger.error(err);
})
.done();

However this is also an anti-pattern, since it will have the promises grow inwards as a promise pyramid of doom and it also promotes catch-done block duplication.

There are a couple more options to write this code differently, but an obvious conclusion is whatever we do with promises, it will be hard to achieve the simple conditional syntax from the sync code example.

Async-Await to the rescue

Async-Await is much easier to reason about and to use when converting conditional sync code to async code. It is also much less verbose, and the syntax is more readable, even if you never worked with it (unlike promises).

Converting our sync code from above is an easy job and it looks pretty much the same as the sync example, except we have the await keywords that indicate "waiting" for a promise resolution, even though under the hood the code works in a non-blocking fashion.

try {  
    const response = await request(data);

    if (response && response.body) {
      const parsed = JSON.parse(response.body); 

      if (parsed) {
        const isSaved = await saveData(parsed);
      }
    }
} catch(err) {
    logger.error(err);
}

So yeah, just take a look at this code and the sync counterpart from the beginning of the article, and you will quickly realize async-await is something you should start using today.

How to start using Async-Await

You have only two options: wait for the ES7 proposal to be implemented in the major browsers, or use it right away with excellent transpilers like Babel.

I am a big fan of transpilers, and for a good reason, since they allow you to use some advanced future language features long before the features are implemented in JS engines and browsers.