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 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.
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.
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.
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 .
- We attach
.catch()
after.then()
to catch any rejected promises.
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.
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 :
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 :
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
.
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.
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
.
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.
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.
const rainbow = async ()=>{
}
- Let's call
delayedColorChange
for the red color . - After the red color, the function should return with "Printed"
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 tilldelayedColorChange('red', 1000)
resolves it's promise.
To do this, we put the
await
keyword in front of thedelayedColorChange
.This will ask the
printRainbow
to wait till the promise is resolved.
const printRainbow = async ()=>{
await delayedColorChange('red', 1000)
return "Printed"
}
- Let us put the rest of the colors in .
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".
To handle the errors we can use try catch blocks
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 .
//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.
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:
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
- We create a xhttp object
-
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. - We use
xhttp.send()
to send the request. - we setup a method
onload
that returnsresponseText
with the data from the request.
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
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.
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.
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
Using npm
$ 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.
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
const main = async ()=>{
try{
let result = await axios.get("url")
return result
}catch{
console.log("request failed");
}
}
Let us make request to a GitHub API using Axios :
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 :
What is the project about
This project is a part of Colt Steele's 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
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.
<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>
The images that we'll get from the API will be displayed within this div.
<div class="shows">
</div>
app.js
Select all the required elements using querySelector.
let input = document.querySelector("#search");
let showdiv = document.querySelector('.shows')
let searchForm = document.querySelector('.search-form')
- 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.
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:
The createImages function below is used to create the images from the API data.
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.
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 :)
Top comments (5)
Great article. Making server requests puzzled the hell out of me when I started learning Javascript.
Perhaps in your next post, you can give async / await a bit more attention and how they go together with promises.
Also, if you're looking for some deeper topics on promises, perhaps you can include
Promise.all()
andPromise.race()
, I'd love to read an article with some use cases about these two guys.Thank you !! I'll keep that in mind
Amazing lecturer!
Thank you!!
Simple and Effective!
This is such a well written article, Congratulations !!
Keep posting more advanced articles.
Thank you!