Magical Mystery Guide For Async/Await In Javascript

Hey there! Today we are going to look at async and await keywords that allow you to pause functions execution, and therefore let you write asynchronous code that reads like synchronous.

But first let’s go through other ways of dealing with asynchronicity in Javascript. You know, just to make you appreciate how async/await allows you to write more readable asynchronous code.

First We Had Callbacks

Imagine that we have some MagicalWorldAPI, and we need to get a list of quests of the hero of some imaginary world.

With callbacks it would look somewhat like this.

getWorld(function(err, world){
  if (err) {
    // handle error;  
    return
  }
  getHero(world.heroId, function(err, hero){
    if (err) {
      //handle error;  
      return
    }
    getQuests(hero.questsIds, function(err, quests){
      if (err) {
        //handle error;  
        return
      }
      console.log(quests);
    }
  });
});

Doesn’t look very good, right? A lot of nesting, also you have to handle errors separately in every callback and it’s kinda error prone. You might forget to add a return statement after you’ve handled an error, or do another silly mistake.

Can we improve this?

Yes, With Promises

Let’s imagine that our MagicalWorldAPI was updated and now it returns Promise objects. Let’s adapt to it.

getWorld().then(function(world){
  return getHero(world.heroId)
}).then(function(hero){
  return getQuests(hero.questsIds)  
}).then(function(quests){
  console.log(quests)  
}).catch(function(err){
  //handle error
});

Now we have a callback in catch function where we can handle errors from any part of that chain. Better, but the code is still hard to read. If only we could make it look synchronous…

Async/Await? Not yet, generators

const co = require('co')

co(function* (){
  var world = yield getWorld();
  var hero = yield getHero(world.heroId);
  var quests = yield getQuests(hero.questsIds);
  console.log(quests);
}).catch(function(err){
  //handle error  
})

OK, the part where we use our imaginary API looks nice now, but the other code is cryptic! What does that * in function declaration do and what are those yield statements?

The asterisk after the function statement makes it create a generator function and the yield keyword pauses generator function execution and the value of the expression following the yield keyword is returned to the generator’s caller.

And co is a nice function that can resolve a generator function and return a promise.

So in this example, the getWorld() returns a promise. Then yield pauses further execution of our star signed function and passes the result of getWorld() to the co function. The co function takes the promise, resolves it and passes the value back to the generator function where it is being assigned to the world variable.

Then the same is repeated for other variables.

Finally we are getting to async/await

Let’s rewrite our code once more.

async function(){
  try{
    var world = await getWorld();
    var hero = await getHero(world.heroId);
    var quests = await getQuests(hero.questsIds);
    console.log(quests);
  }
  catch(err){
    //handle error
  }
}

Looks familiar, right? We just changed yield to await, instead of fuction* we now have async function statement and we don’t use the co function here.

Oh, and another thing, we now use the try/catch to handle errors. This is good, because we can now handle both synchronous and asynchronous code errors the same way.

So what happens here?

The async function statement defines an asynchronous function. When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception, the Promise will be rejected.

Also an async function can contain an await expression, that pauses the execution of the async function and waits for the passed Promise’s resolution, and then resumes the async function’s execution and returns the resolved value.

The execution flow will go a lot like in previous example. When we’ll stumble upon the first await statement – our async function will get paused until the getWorld() promise will be resolved. Then async function will get unpaused and the resolved value will be assigned to the world variable.

Then the same will be repeated for other variables.

Summary

Today we’ve learned that using async statement you can create asynchronous function.

Inside that function you can use the await statement in front of expression that returns a Promise.

When the async function will by executed, it will pause just where the await statement is until that Promise is resolved.

And also we’ve learned that using async/await you can simplify reading of an asynchronous code by giving a more synchronous flow to it.