Now that we have a little more experience with HTTP requests let's look in more depth at using promises and async/await to make multiple requests, both in parallel and in series.
A blog about cats
In this chapter, you are going to create a random blog post generator, using an API created for this course. Your app will make three parallel GET calls, followed by a final POST call. This should show you the real value of async/await.
Practice!
Head to CodePen Exercise P3CH3a and follow the instructions below.
Seeing as we will be making four HTTP requests, it would be far better to make one function that we can call four times, rather than to write out very similar code four times. Let's build our makeRequest()
function.
Create a new function called
makeRequest()
which accepts a verb, a URL and a data object as arguments and returns a Promise.Create an
XMLHttpRequest
object calledrequest
, and call itsopen()
method, passing theverb
andurl
arguments from the overall function call.Implement our request's
onreadystatechange
function. Status codes 200 and 201 are success codes, anything else is an error — remember, this only matters for a ready state of 4. For a successful request, the Promise will resolve with the response as an object. For an unsuccessful one, it will reject with the response as an object.If the request type is 'POST', set the request's
Content-Type
header toapplication/json
and send the request with the stringified data object. Otherwise, simply send the request.Check your
makeRequest()
function with the screencast.
Once you've given it a go, watch me code a solution and see how your approach compares:
Now let's use async/await to put everything together.
Practice!
Head to CodePen Exercise P3CH3b and follow the instructions below.
Let's build an async
function to make the requests we need and print the final result to the DOM.
We could make and await each request, one after the other. However, this method isn't very performant, as the overall time will be far longer. Instead, we are going to use Promise.all
, a method which allows Promises to run in parallel and which resolves when all of the Promises we pass it have resolved.
Create an async function called
createPost()
. Here are the API endpoints we will be needing:GET /generate-uid — returns
{ uid: string }
GET /generate-title — returns
{ title: string }
GET /generate-lorem — returns
{ lorem: string }
POST /create-post-with-uid
requires
{ uid: string, title: string, content: string }
returns
{ message: string, post: { id: string, title: string, content: string } }
Create three Promises —
uidPromise
,titlePromise
andcontentPromise
— using themakeRequest()
function and passing it the'GET'
verb and the appropriate URLs.The
Promise.all
method takes an array of Promises as an argument, and returns an array containing the resolved values of each Promise. We could do this:const responses = await Promise.all([uidPromise, titlePromise, contentPromise]);
However, that would give us a
responses
array, leading to less readable code (what exactly isresponses[2]
?). Instead, we'll use destructuring syntax. Add the following line:const [uidResponse, titleResponse, contentResponse] = await Promise.all([uidPromise, titlePromise, contentPromise]);
This creates separate named constants for each resolved Promise whilst still leveraging
Promise.all
. Pretty awesome, right?Now we need to collate the various responses to create our final POST request. Don't forget: each resolved Promise currently contains the object returned by the server, not just the text. Looking back at the API docs, create a Promise called
postPromise
usingmakeRequest()
, passing the verbPOST
, the correct URL, and the required kind of data object, containing the data extracted from the previous three requests.Now, using
await
, create a constant calledpost
to hold the POST request's response data.Finally, extract the blog post data from
post
and display it in the DOM, in the text content ofpostTitle
,postId
andpostContent
.Now all we need to do is hook up our button! Add a click event listener to our Generate Post button and use it to call
createPost()
. Hit Show result, click the Generate Post button and see what happens!
Once you've given it a go, watch me code a solution and see how your approach compares:
Let's recap!
In this chapter, we looked at how to use async/await to handle parallel and serial HTTP requests. We looked at how Promise.all
, along with destructuring syntax, is a powerful tool for leveraging multiple simultaneous AJAX requests.
What we did not do correctly, however, is error handling. We have a reject() call in our promise, but no way of handling any errors returned by the server. There are also other potential bugs because we have not really thought about what could go wrong.
Not to worry though: the next chapter looks in a bit more detail at error handling.