Crash course in Asynchronous JavaScript (Part 2)
Tags: javascript, programming, webdev
Published: 2021-05-04 04:16:32


# Introduction
Welcome back! 

Here is what we'll cover in part 2
- Promises
- Async and Await
- Making Http Request
- API Project

Let us continue from where we left off. We'll start by escaping the callback hell.

# Promises
## What is a promise?

![promise](https://user-images.githubusercontent.com/51131670/116772566-92717b80-aa6d-11eb-8b71-9fbd369c17c0.jpg)
 
Promise in JavaScript is like the word promise in the real world . When we promise something there are two things that can happen either the promise is kept(resolved) or the promise is broken(rejected).

Similarly, The Promise object in JavaScript has 3 states :

**pending** : initial state, neither resolved nor rejected.

**resolved** : meaning that the operation was completed successfully.

**rejected** : meaning that the operation failed.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn2rkwrxds2gde5vm8ai.png)
 

**Definition :** Promise in JavaScript is defined as an object that represents the eventual completion (or failure) of an asynchronous operation . 

Woah! So lets break that down. 
- Promise is an object . 
- It is often used during Async operations. 
- Promise objects are returned by asynchronous functions which may not have a value initially but will have the value at some point in the future. 


Let us understand this with a fake function.

```javascript
const fakeRequest = (url) => {
    return new Promise((resolve, reject) => {
        const rand = Math.random();
        setTimeout(() => {
            if (rand < 0.7) {
                resolve('YOUR FAKE DATA HERE');
            }
            reject('Request Error!');
        }, 1000)
    })
}
```
 This function is basically trying to simulate a response from a remote server. When we request data from a server, it might take few seconds before it completes or rejects our request. In a sense, the server is promising us to respond with the data . 

When the function is called, a Promise is made by the function. It can either keep(resolve) the promise returning the data or break(reject) the promise and throw an error.

This function is generating a random number between 0 and 1. If the number is less than 0.7, it resolves this promise by using the resolve function with the data as parameter, else the promise is rejected with an error in the reject function.

Now that we have created the Promise , How do we consume it?

- We use `.then()` , `.catch()` on the function calls to consume the promise .

 
## .then 
- We pass a callback function to `.then` which is executed when the promise is **resolved**.
```javascript
fakeRequest("fakeurl").then((data)=>{
 console.log(data)
})
```
- The 'data' argument will contain 'YOUR FAKE DATA HERE' if the promise is resolved.

## .catch
- If the promise is rejected, we'll have to catch the error .

![catch](https://user-images.githubusercontent.com/51131670/116789618-54a43f80-aacd-11eb-8fc5-ca0b30837538.gif)

- We attach `.catch()` after `.then()` to catch any rejected promises.

```javascript
fakeRequest("fakeurl").then((data)=>{
 console.log(data)
}).catch((error){
 console.log(error)
})
```
- If any error is thrown, 'error' argument will have the value 'Request Error!' .

- To put is simply `.then` is for resolve , `.catch` is for reject.


The main difference between callback functions and promises is that, in promises you attach the callbacks(.then and .catch) to the returned promise object and in callback functions you pass callbacks to the function as arguments(success and failure) . 

Do you remember the rainbow function we made using callbacks?
Let's recreate it using promises.

```javascript
const delayedColorChange = (color, delay) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            document.body.style.backgroundColor = color;
            resolve();
        }, delay)
    })
}
```
Now, if we want the colors to appear one after the other, we just chain the `.then` in order. Since, the code within each `.then` only runs when the previous promise is resolved, it allows us to perform multiple asynchronous tasks (changing background color) synchronously(one after the other).

Something like this :
```javascript
delayedColorChange('red', 1000)
    .then(() => delayedColorChange('orange', 1000))
    .then(() => delayedColorChange('yellow', 1000))
    .then(() => delayedColorChange('green', 1000))
    .then(() => delayedColorChange('blue', 1000))
    .then(() => delayedColorChange('indigo', 1000))
    .then(() => delayedColorChange('violet', 1000))
```
- First delayedColorChange will be called for color red.
- When it finishes and resolves the promise ".then" delayedColorChange will be called for color orange and so on.

Let's see a real world situation.

- Sometimes you may need to make multiple API calls or calls to your database for data one after other.
- Lets assume you have to make calls to two APIs 'A' and 'B', but to call 'B' you'll need to pass data that you get from 'A' .
- So 'A' needs to be resolved first, right? `.then` "B" can be called.

- We can use our `fakeRequest` function to make these fake API requestS.
- Here is how we do it :
```javascript
fakeRequest('urlA')
    .then((dataa) => {
        console.log("(urlA) worked")
        return fakeRequestPromise('UrlB')
    })
    .then((datab) => {
        console.log("(urlB) worked")
    })
    .catch((err) => {
        console.log("OH NO, A REQUEST FAILED!!!")
        console.log(err)
    })

```
- This works because `.then` only runs after promise is resolved .
Then we call the urlB API .
- Now, the next `.then` is attached for handling urlB
- If any one of the requests fail (promises are rejected), they just fall through to the `.catch`.
- If we want to have more API calls after urlB we just keep on returning the functions and chaining the `.then`.

```javascript
fakeRequest('urlA')
    .then((dataa) => {
        console.log("(urlA) worked !!!")
        return fakeRequestPromise('urlB')
    })
    .then((datab) => {
        console.log("(urlB) worked !!!")
        return fakeRequestPromise('urlC')
    })
    .then((datac) => {
        console.log("(urlC) worked !!!")
    })
    .catch((err) => {
        console.log("OH NO, A REQUEST FAILED!!!")
        console.log(err)
    })
```

The reason for doing this is to simplify the code . We see that both the rainbow function and the fake request function look a lot simpler when we use promises compared to the functions made using callbacks. 


# Async/Await

- Async and Await are what programmers call syntactical sugar.
- It is basically an easier way of using promises.

 ## Async 
The async keyword can be placed before a function.
```javascript
const greeting = async ()=>{
   return "Nice to meet you!!"
}
```
- Writing the async keyword means that now, the function always returns a promise.
- No matter what kind of values are returned by the function, it is always wrapped in a promise.
- So, now that the function returns a promise, we can call it using `.then` .
```javascript 
greeting().then((data)=>{
 console.log(data)
})
``` 
- This will print out "Nice to meet you!!".
- Thus we do not need to explicitly resolve the promise .

 ## Await
- Await keyword pauses the code within the async function till the promise is resolved.
- Await is only used within an async function .
- If we use the Await keyword outside the async function it will be a syntax error. 

```javascript
 let result = await promise;
```

- Let us call our rainbow function `delayedColorChange()`but now using async and await . 

- First, we create an async function that will call the functions for the different colors.
```javascript
const rainbow = async ()=>{

}
```
- Let's call `delayedColorChange` for the red color .
- After the red color, the function should return with "Printed"
```javascript
const printRainbow = async ()=>{
  delayedColorChange('red', 1000)
  return "Printed"
}
```
- However, if we execute it like this, the function won't wait for the red color to be displayed . It will instantaneously print "Printed".
- Thus we must ask the `printRainbow` function to wait till `delayedColorChange('red', 1000)` resolves it's promise.
![waiting](https://user-images.githubusercontent.com/51131670/116772567-930a1200-aa6d-11eb-8f13-a33b02a49452.gif)

- To do this, we put the `await` keyword in front of the `delayedColorChange`.
- This will ask the `printRainbow` to wait till the promise is resolved.
```javascript
const printRainbow = async ()=>{
  await delayedColorChange('red', 1000)
  return "Printed"
}
```
- Let us put the rest of the colors in .
```javascript
const printRainbow = async ()=>{
  await delayedColorChange('red', 1000)
  await delayedColorChange('orange', 1000)
  await delayedColorChange('yellow', 1000)
  await delayedColorChange('green', 1000)
  await delayedColorChange('blue', 1000)
  await delayedColorChange('indigo', 1000)
  await delayedColorChange('violet', 1000)
  return "Printed"
}

printRainbow().then((data)=>(
  console.log(data)
))
```
- After all the colours are shown the console prints "Printed".<br>


To handle the errors we can use try catch blocks
```javascript
const printRainbow = async ()=>{
   try{
        await delayedColorChange('red', 1000)
        return "Printed"
   } 
    catch{
        console.log("error")
    }
}
```

We can compare it with the callback version of the function .

```javascript
//Promises (Async and Await)
const printRainbow = async ()=>{
  await delayedColorChange('red', 1000)
  await delayedColorChange('orange', 1000)
  await delayedColorChange('yellow', 1000)
  await delayedColorChange('green', 1000)
  await delayedColorChange('blue', 1000)
  await delayedColorChange('indigo', 1000)
  await delayedColorChange('violet', 1000)
  return "Printed"
}

//Callbacks
delayedColorChange('red', 1000, () => {
    delayedColorChange('orange', 1000, () => {
        delayedColorChange('yellow', 1000, () => {
            delayedColorChange('green', 1000, () => {
                delayedColorChange('blue', 1000, () => {
                    delayedColorChange('indigo', 1000, () => {
                        delayedColorChange('violet', 1000, () => {
                             //This function will be empty since 
                             //we want to end the 
                            //color change
                        })
                    })
                })
            })
        })
    })
});

```
As we can see it is much cleaner and compact.



# Making HTTP request
- Let us now understand the final piece in the puzzle of async JavaScript.
- We know how to handle the responses coming from APIs using promises and callbacks.
- Now, it is time for us to make an actual http request rather than faking it using `fakerequest()` function.

## Request header
HTTP headers are basically ways to pass additional information between the client and server.
![headerreq](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ti689gcsw7t78wb3iwqy.PNG)
We can see additional information about the request that is passed through headers are in the form of key and values.

## Types of http requests

- GET     : Used to "Get" the data from a resource.
- POST    : Used to send data to a particular destination.
- PUT     : Used to update the existing data.
- DELETE  : Used to delete data.

Mostly we will be using `GET` request, since we want to get the JSON data from the API.

In the Web API section, the request we made through the browser to the Pokémon API  was a `GET` request, since we 'get' the image data from the API server.

However, to use the data from the web API in our own website we have to make requests through JavaScript.

There are different ways to make request to these web APIs Asynchronously through JavaScript. 

- XMLHttpRequest
- Fetch API
- Axios

We'll be using Axios for our webpage. However, let's have an overview of XHR and fetch API as well. 

## XMLHttpRequest
- XHR is the original way of making requests using JavaScript.
- It is a browser API.
- It is not a preferred method since it uses callbacks and does not support promises.

The basic syntax is as follows:

```javascript
var xhttp = new XMLHttpRequest();//1
xhttp.onload = function() {//4
  const data = JSON.parse(this.responseText);
};

xhttp.onerror = function() { // only triggers if the request couldn't be made at all
  alert(` Error`);
};
xhttp.open("GET", "filename", true);//2
xhttp.send();//3
```
1. We create a xhttp object
2. `xhttp.open("GET", "url", true);` opens the request by specifying : type of request, url that is to be requested, if we want the request to be async or not.
3. We use `xhttp.send()` to send the request.
4. we setup a method `onload` that returns `responseText` with the data from the request. 

<hr>

## Fetch API

Fetch makes it easier to make web requests and handle responses compared to the older XMLHttpRequest.

It is a simpler API which uses promises, avoiding callback hell and having to remember the complex API of XMLHttpRequest.

### Syntax
```javascript
fetch('http://example.com/movies.json')
  .then(response => response.json())
  .then(data => console.log(data));
```
We pass the URL to the fetch function which returns a promise with the response. However, this is just a HTTP response and not the actual JSON data. To get the JSON content we use `.json` method on the response. Finally we print the data in the console.

<hr>

## Axios
- Axios is a promise based HTTP client for the browser and node.js.
- It is an improvement on the fetch api.

### Installing
Axios can be added using a CDN as well as through node.js :

### Using CDN

We can attach the below script tag to our html document above our own js file to use Axios.
```HTML
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
```
<hr>

### Using npm
```javascript
$ npm install axios
```

## Usage

### Get Request
Making a GET request is as simple as writing `axios.get(url)` . To handle the responses we use `.then()` and `.catch()` since axios uses promises.
we do not need to chain another `.then` like the fetch API.
```javascript
axios.get('API URL')
  .then(function (response) {
    // handle success
    console.log(response);
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })
```

We may also use async await 

```javascript
const main = async ()=>{
    try{
        let result = await axios.get("url")
        return result
    }catch{
        console.log("request failed");
    }
}
```
<hr>

Let us make request to a GitHub API using Axios :

```javascript
axios.get('https://api.github.com/users/mapbox')
  .then((response) => {
    console.log(response.data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
  });

// logs:
// => {login: "mapbox", id: 600935, node_id: "MDEyOk9yZ2FuaXphdGlvbjYwMDkzNQ==", avatar_url: "https://avatars1.githubusercontent.com/u/600935?v=4", gravatar_id: "", …}

// => 200

// => OK

// => {x-ratelimit-limit: "60", x-github-media-type: "github.v3", x-ratelimit-remaining: "60", last-modified: "Wed, 01 Aug 2018 02:50:03 GMT", etag: "W/"3062389570cc468e0b474db27046e8c9"", …}

```

We can also make other requests using Axios .

- `axios.request(config)`
- `axios.get(url[, config])`
- `axios.delete(url[, config])`
- `axios.head(url[, config])`
- `axios.options(url[, config])`
- `axios.post(url[, data[, config]])`
- `axios.put(url[, data[, config]])`
- `axios.patch(url[, data[, config]])`

The some parameters are put into square brackets because they are optional.

# API Project
Now, it's time to put together everything we have learnt so far .

Here is what we will be building :
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vlf7k533feu1zo85qxll.png)

## What is the project about 
 This project is a part of [Colt Steele's](https://www.udemy.com/share/101W9CB0UTcF1bR3Q=/) web developer bootcamp course.
It is a website that display tvshows according to the user's input using the tvmaze api.

# Code

Here is a link to the completed project : [TvShowApi](https://github.com/ChinmayMhatre/TvShowApi-request)


# index.html

- We create a form and give it a class of `search-form` to be used later in JavaScript.
- Add a text input to get the data.
- A submit button to submit the form.
```html
    <form class="search-form" >
    <input type="text" class="form-control" id="search" placeholder="Search Tvshows">
    <input type="submit" class="form-control mt-2" id="sub" value="Submit">
    </form>
```

<hr>

The images that we'll get from the API will be displayed within this div.
```HTML
<div class="shows">   
</div>
```

<hr>

# app.js

Select all the required elements using querySelector.
```javascript
let input = document.querySelector("#search");
let showdiv = document.querySelector('.shows')
let searchForm = document.querySelector('.search-form')
```
<hr>

- We create an event listener which makes the axios request asynchronously each time the form is submitted.
- After getting the data from axios we pass it to a `createImages` function which is used to display the images of the different shows we get back from the API.

```javascript
searchForm.addEventListener("submit",async (e)=>{ //e is a event object 
    e.preventDefault(); //Prevents form from refreshing the page.
    if(input.value != ""){ // Checking if the input is empty.
        try {
            let result = await axios.get(`http://api.tvmaze.com/search/shows?q=${input.value}`)
            createImages(result.data)//result.data is an array
            console.log(result); // You can look at the result from the api in the console
        }
        catch (error) {
            console.log(error);
        }
    }
})
```
Here is how the response from the API looks like: 
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r2e6z7r7ke58ypyblmnh.png)

<hr>

### The createImages function below is used to create the images from the API data. 
```javascript
const createImages = (shows)=>{//shows is an array
    for(show of shows){ 
        if (show.show.image) { // checking if there is an image for the current show
            let image = document.createElement('img')
            image.src = show.show.image.medium // show.show.image.medium contains the url of the image
            showdiv.append(image) //we attach the images to an empty div that we created in html
        }
    };
}
```
Below image shows the path which we used to get the image url in the `createImages` function.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2tytvniz2ci9afzd4fua.png)


# Conclusion
I hope after reading this, you'll have a better understanding for the different puzzle pieces in the jigsaw puzzle of Async JavaScript.

Now, the next step from here on out would be to try the different concepts in your own code. You can also try out the different code snippets provided in the article and observe the result you get.

Let me know in the comments if you found this helpful . 

See you in the next one :)