Learn ES6 Promise and ES7 Async/Await

Ironhack Logo

Asynchronicity

(ES6 Promises and ES7 Async/Await)

Assuming you already know:

Learning Goals

After this lesson, you will be able to:

  • Understand why do we need to work with ES6 Promises
  • The ES6 way to manage Promises
  • How to deal with ES6 Promise errors
  • Implement your own ES6 Promises
  • A sharper way to deal with Promises with ES7 syntax

Introduction

in the beginnings there was no Promises and everything was just caos and this was known as the callbackHell... then ES6 and its Promises arrived...

The Callback was the standardized way to do something when the task was completed, but it was very difficult to read and maintain.

function handler(request, response) {  
    User.get(request.user, function(err, user) {
        if (err) {
            response.send(err);
        } else {
            Profile.get(user.profile, function(err, profile) {
                if (err) {
                    return response.send(err);
                } else  {
                    doSomethingAsync(user, profile, function(err, result) {
                        if (err) {
                            response.send(err)
                        } else {
                            response.send(result);
                        }
                    });
                }
            });
        }
    })
}

There were a lot of suggestions to manage this, but the most popular and the one that becomes the new standard was Promises

Understanding the ES6 Promise

Section 1

When we talk about Promises there are always two sides, one defending it and the other hating it, but thats because we do not understand the asynchronicity from javascript, or may be we have learned to code in a synchronous language like Python or C#
The core concept of asynchronicity is that javascript does not wait for you, it keeps reading line by line of your code even if your code will take a while to acomplish the read line, javascript just continue it's single threaded.

That's why if we code something like this:

const getNumber = function(){ return setTimeout(Math.random,3000); }; console.log(getNumber());

We receive a undefined as response, why? if we had declared it a function and we have execute it?. Well, Javascript does not wait for the timeout.

One way to solve this issue is to tell javascript ok, i am not ready, i have to wait for 3000 milisenconds and then i will be ready to give you a random number, the way to say that to javascript is with a Promise, it is just like give an exchange cupon, or may be a promissory note for money like a check with certainly date to pay the rent =P. something like this:

const getNumber = function(){ return new Promise(function(resolve, reject){ setTimeout(()=>resolve(Math.random()), 3000); }); } console.log(getNumber());

If we run this code, we can see we now receive a Promise instead of undefined, but we still haven't receive the number we want.

The syntaxis that we have use for make a Promise is this:

new Promise(function(resolve, reject){}) that could be simplified with the arrow function of ES6 to:
new Promise((resolve, reject)=>{})

The resolve parameter gives this positive response to exchange it for the right answer or value, while the reject parameter can be used for tell that something goes wrong and we won't be able to fulfill the bill and will sleep at the street for not paying the rent
=S

homeless

The right way to get our number, complete our Promise and pay the rent would be:

const getNumber = function(){ return new Promise(function(resolve, reject){ setTimeout(()=>resolve(Math.random()), 3000); }); } getNumber().then(number=>console.log(number));

With some sugar syntax from ES6 would be like:

const getNumber = () => new Promise(res => setTimeout(()=>res(Math.random(), 3000)));  

notice that the change is not in the getNumber function but in the way you call it. You call it and then (when the Promise come to resolve this is when the 3000ms has passed, the Promise reolve a random number) and we can receive that number in a function inside the .then() method.

As i mention before, the ES6 Promise has the parameter reject that is a handler to return a negative response and usually instead of returning something return you can use the reject handler directly or throw an error, this is like so because is easier to manage the negative response with a .catch() method

const makeError = new Promise(function(resolve, reject) {  
      reject("error");
   }
});
const makeError = new Promise(function(resolve, reject) {  
      throw new Error("error");
   }
});

do not worry we are about to use it in a second.

Using Promises

Subsection 1.1

Now that you can pay the rent, and know how ES6 Promises works, it's time to use them!

Use Promises in real world

Lesson Flow

I Need to Fetch my Data from the Github Server

I DO (iknow this does not need to apear XD)

Let's say i need to build a presentation page and i need my Github info, my repos, starts and all the pro stuff, and i will use the Github API from this url: "https://api.github.com/users/username", where the username will be my own.

So i use the fetch library to make a http request like this:

const fetch = require("node-fetch"); const url = "https://api.github.com/users/hectorbliss"; console.log(fetch(url))

As you can see if you run the code, it returns a Promise.

Well we have to manage the response propertly.

this library (Fetch) has its own way to return the data, after this call we need to call in the response the method .json() that returns another Promise

Approaching the Challenge with .then()

WE DO (iknow this does not need to apear XD)

We can make this call and chain a .then() method to get the response, just like we did before:

const fetch = require("node-fetch"); const url = "https://api.github.com/users/hectorbliss"; fetch(url).then(response=>{ console.log(response); });

As i mention the response now comes with some information about our HTTP request. but there is another Promise too.
Then we can use our new skill to chain another .then() method and see what happen.

const fetch = require("node-fetch"); const url = "https://api.github.com/users/hectorbliss"; fetch(url).then(response=>{ console.log(response); return response.json(); }) .then(data=>{ console.log(data); });

Nice, you can now get the info from the coders you follow, and admire! for me i can test this with Dan Abramov's profile (creator of Redux) and get his avatar_url

const fetch = require("node-fetch"); const url = "https://api.github.com/users/gaearon"; fetch(url).then(response=>{ console.log(response); return response.json(); }) .then(data=>{ console.log(data.avatar_url); });

Now it is your time to test it, and catch any errors.

What about the errors ?

YOU DO (iknow this does not need to apear XD)

We have discussed about the errors, it is time to generate some of them and catch

1.- Make this request with a non-existent name like "ir0nh4ck"
2.- Use the attribute ok to see if the answer from the server is good if not, throw an error.
3.- Once you have thrown an error catch it and send it to the console.

DO NOT SEE THE NEXT CODE UNTIL YOU HAVE TRIED TO SOLVE THE CHALLENGE

To solve the challenge we can do something like this:

const fetch = require("node-fetch"); const url = "https://api.github.com/users/ir0nh4ck"; fetch(url).then(response=>{ console.log(response.ok); if(!response.ok){ throw new Error("El usuario no existe"); } return response.json(); }) .then(data=>{ console.log(data.avatar_url); }) .catch(err=>{ console.log(err); });

This is really nice! you have done it just good! surely with your sharp new skills you will never fail in pay the rent =D

Additionally to this we can ask for several Promises in one single call with Promise.all[promise1, promise2, promise3].then(listOfValues)

What about some ES7 syntax? let's "go back to the future."

Section 2

nowadays it is very important to be aware of what we can do easily with syntax that is coming or is actually here and widely used in the javascript environment, just like Async/Await from the ES7 syntax and that is completely stable.

We can change our .then() method chain for something like this:

const fetch = require("node-fetch"); const url = "https://api.github.com/users/gaearon"; async function doFetch(){ const response = await fetch(url); console.log(await response.json()); } doFetch();

As you can see, this reduces considerably the amount of code, there is no identation and is easier to read, what have changed on this code?

1.- We have put the word async before the declaration of the function

2.- We have declared a const to store the result of the promise request instead of chain a .then() method.

3.- And finally is possible to call response.json() with no variable inside the console.log() and it will wait for the Promise

and we can trate this async function just like a normal Promise and chain a .then() method to them:

const fetch = require("node-fetch"); const url = "https://api.github.com/users/gaearon"; async function doFetch(){ const response = await fetch(url); return await response.json(); } doFetch().then(r=>console.log(r));

Wait, what about erro handling?

There is many ways to handle errors in async/await some very elegant and transparent but the quick one is to use a try catch block like this:

const fetch = require("node-fetch"); const url = "https://api.github.com/users/perrote"; async function doFetch(){ try{ const response = await fetch(url); if(!response.ok) throw new Error("El usuario no existe") return await response.json(); }catch (err){ return err } } doFetch().then(r=>console.log(r)).catch(err=>console.log(err));

Async/await is one of the most revolutionary features that have been added to JavaScript in the past few years. It makes you realize what a syntactical mess promises are, and provides an intuitive replacement.

As final though, you also can convert the .then() chain into an async function.

const fetch = require("node-fetch"); const url = "https://api.github.com/users/hectorbliss"; async function doFetch(){ try{ const response = await fetch(url); if(!response.ok) throw new Error("El usuario no existe") return await response.json(); }catch (err){ return err } } //doFetch().then(r=>console.log(r)).catch(err=>console.log(err)); async function getUser(){ console.log(await doFetch()); } getUser();

UFF too much information BlisS!

naa, remember you are here to become a badass hacker!

Independent Practice (optional)

Now that you have gone through all these topics, it's your turn to show what you got

  1. You are now working for Disney and you are in charge to build a promotion landing page for the brand new film "The last Jedi", with the profile of Luke Skywalker. But you are not a very fan of it (shame on you!) and you do not know the info of the Luke character, but luckily there is an API for that and you will use it!:
    https://swapi.co/api/people/1

  2. with this API you will build a javascript file with the correct chain of Promises and/or the necessary async functions, you can use whatever you prefer.

  3. As an extra, you can show that information in a DOM element. Can use QuerySelector or jQuery ;)

You can push your gist to the provided repo for review.

Summary

How can you use this right now? Babel.
If you aren't already using Babel, it's easy to install. And Promises and await is supported out of the box in the latest version! and is Now available natively in Node v7.6.0 and later!

You have learned a very importat concept and skill like is ES6 Promises that will let you work with asynchronous resources from servers and local methods, and finally you have dealt with errors into ES6 Promises

now yo know:

  • How to work with ES6 Promises
  • ES6 syntax for managing ES6 Promises
  • You have implemented your own Promises
  • In the mid way you have used ES6 Promises to get data from a real server
  • And finally you know how to work with Promises with ES7

Congrats!

Extra Resources