- Lab
- Core Tech

Using Promises with JavaScript
In this hands-on lab, you'll learn how to create and consume promises, which are a way to eliminate passing around callbacks and write code that is able to express what is happening behind the scenes. You'll learn about the three states of each promise: pending, fulfilled (or resolved), and rejected. You'll also create a promise to replace the callback for an HTTP request. By the end of the lab, you'll also use axios, a promise-based HTTP client for JavaScript, to consume a promise.

Path Info
Table of Contents
-
Challenge
Introduction
Promises are a way to eliminate passing around callbacks and write code that is able to express what is happening behind the scenes.
Every promise has one of 3 states:
- pending - This is the initial state
- fulfilled or resolved - The operation completed successfully
- rejected - The operation failed
HTTP calls are great opportunities to use promises. While the request is executing, the promise will be in the
pending
state. If the request succeeds (i.e. sends back a 200 code), then the promise will befulfilled
. If the request fails (e.g. a 404 or 500 error), then the promise will berejected
.In this course, you'll start by creating a promise that we'll use to replace the callback for an HTTP request.
Once you've learned how to create promises, you'll move on to consuming them. You'll do that by using axios, which is a
promise
based HTTP client for JavaScript that follows the same rules as above. That is, when an HTTP call is successful,axios
willresolve
the promise. When an HTTP call is unsuccessful, for example, when it returns a 404,axios
willreject
the promise.And because each
axios
function returns a promise, you are able to chain multiple calls together.We'll cover each of these examples, and more, in the following steps.
-
Challenge
Create and resolve a promise
Open the
created.promises.js
file, which we'll use for the first two steps. Notice thecustomPromise
function. It uses the node module,http
to perform aget
request to get anorderStatus
by its id.This code is asynchronous. That is,
http.get
will not return the data, instead uses a callback (thecb
variable) to send either a successful messageres.on('data', (chunk) => { cb(JSON.parse(chunk)); });
Or handle the case where the call is not successful on line 11.
cb(res.statusCode);
Instead of using a callback, we'd like this function to use a
promise
so that the consumers don't have to pass around a callback.Promises are plain JavaScript objects. As a result, the first step in using a promise is creating a new promise. The constructor takes a function, and the first parameter of that function is another function most commonly named
resolve
new Promise((resolve) => {}) ``` Now that you've created a promise, it's time to tell the promise to resolve. Use the `resolve` function that is passed in to the constructor.
-
Challenge
Create and reject a promise
Just like a promise can be
resolved
, it can also berejected
. This is how you will communicate to your consumers that the promise failed.As you've already seen, when you create a promise, you pass in a function that takes a parameter commonly named
resolve
new Promise((resolve) => {});
The function can also take a second parameter commonly called
reject
.new Promise((resolve, reject) => {})
-
Challenge
Handle a resolved promise
Now that you've created a promise and been responsible for either resolving or rejecting it, it's time to move on to the more common use case: consuming promises.
To start, navigate to the file
http.promises.js
. We'll use this file for the next several steps.Start with the function
getOrders
. This function usesaxios
get a list of available orders in our e-commerce system.Since
axios.get
returns a promise, the code on line 6 is incomplete. This linereturn axios.get('http://localhost:8888/orders');
does not return the orders, but rather a promise that will eventually resolve with orders.
To have
getOrders
actually return the orders, we need to handle the case where the promiseresolves
.When a promise
resolves
it calls a function namedthen
, which itself takes a function. For example:.then(result => { });
One thing to know about
axios
is that the response that it returns contains more than just the data. It also contains meta-data such as thestatus code
,url
, and more. It stores the data in a property nameddata
in that response. -
Challenge
Chain resolved promises
Promises can be chained together, this means that you can have successive
then
functions.That can be helpful, particularly when you need to make one call before you can make another call. For example, in our e-commerce application, we might fetch a specific order and then with that information, look up an additional piece of data.
There's not really anything special with the syntax of chaining
then
functions. They'll look something like:.then(response => {}) .then(response => {})
There's also not really a limit on the number of promises you can chain together!
-
Challenge
Handle a rejected promise
So far you've seen how promises work when everything goes right, but what about when there's an error?
With HTTP requests, like we're doing with
axios
, an error could be anInternal Server Error
(a 500 code), or even something like the404 - Not Found
status. It's important to know if your HTTP call succeeded.Promises help with this as well. Just like the
then
function, promises offer a second function,catch
that will handle errors. Syntactically, it's very similar.catch(err => {});
To test this out, make a call to get an
order
with an id of -1. That order doesn't exist, and so the server will return 404, and theaxios
promise will fail. -
Challenge
Execute code after a promise is settled
Oftentimes you will want to perform some kind of action after your promise runs regardless of if it succeeded or failed. For example, after making an HTTP call, you might want to turn off a "loading indicator" so that the user can continue using the application.
With promises, this state is called
settled
. That is, the promise has executed and has either beenfulfilled
orrejected
and so the promise is now in thesettled
state.You've already seen that promises use a
.then
function for thefulfilled
result and a.catch
for therejected
result. So it shouldn't be too surprising that there's a third function for thissettled
state. That function is namedfinally
and it is used very similar to the other functions..finally(() => {});
-
Challenge
Wait for all calls to succeed
Up until now you've been working with a single promise at a time, and that will be helpful in handling asynchronous code. However, one aspect of promises that are very powerful is the ability to queue up multiple promises so that they can run in parallel.
Open up the
multiple.promises.js
file.Look at the function
loadPartialMetadata()
, and you can see three promises assigned to three variables on lines 4-6let categories = axios.get("http://localhost:8888/itemCategories"); let statuses = axios.get("http://localhost:8888/orderStatuses"); let userTypes = axios.get("http://localhost:8888/userTypes");
Remember that
axios
returns promises. Socategories
,statuses
anduserTypes
are not the data from the API, but rather promises that point to that call.Each of these API calls return metadata that your users will use throughout the app. There's no business case for why one of those API calls should wait on the others. That is
itemCategories
doesn't depend onorderStatuses
.At the same time, it doesn't make sense to allow the program to continue until all of the calls have returned their data.
The
Promise
object accounts for this with a variety of functions that can be used for queuing up promises.The first one to look at is
Promise.all
. This function accepts a list of promises and then willresolve
when all promisesresolve
.Promise.all([promise1, promise2, ... promiseN]) ``` Since `Promise.all` creates a new promise, it will now behave the same as the other promises you've worked with. That means that when it `resolves` it will still call a `.then` function. One important piece to know is what the response data will be inside your `.then` function. `Promise.all` resolves with an array of data that is in the same order as the array it was called with. That is, in this code block: ```JavaScript Promise.all([promise1, promise2]) .then(result => {})
The variable
result
will be an array[response1, response2]
The
loadPartialMetadata
needs to make a make sure that it loadsitemCategories
,orderStatuses
, anduserTypes
. However, for performance reasons, it does not want to do so sequentially. It's important to note one thing about the behavior of.all
. It willresolve
when all of the promises have resolved. However, it will fail the instant that any one of the promises isrejected
.For example, if
categories
succeeded, andorderStatuses
failed,Promise.all
would not wait around to see whatuserTypes
would do, it would immediately reject the promise and your.then
block would not be called. You would need to handle that case by chaining a.catch
function.However, in the next step, you'll learn about another function on the Promise object that can handle this case.
-
Challenge
Wait for all calls to settle
Inside the
multiple.promises.js
file there's a second function namedloadAllMetadata
. This function looks very similar to theloadPartialMetadata
function from the last step.However, there is one difference, and that is, there's a new metadata type,
addressTypes
.In the API,
addressTypes
do not exist. So if you tried to load that metadata you would get a404 - Not Found
response from the API.If you were to use
Promise.all
, like in the last step, you would never be able to load your metadata, becauseaddressTypes
would fail. While that might be useful in some cases, it's not always useful. Sometimes you would like to have whatever data you can, and move on, even if all the promises don't resolve.For that, there's the function
allSettled
. Likeall
, this function creates a newpromise
. This newpromise
will onlyresolve
once all the promises that are passed in aresettled
(eitherresolved
orrejected
).The function is called in the same way as
all
, by passing in a list of promises.The data that is returned is slightly different, however. Like
all
the data will be returned in the same order it was called. Unlikeall
, though, each returned value will have astatus
property that will be eitherfulfilled
orrejected
The
loadAllMetadata
function needs to handle the case whereaddressTypes
fails whileitemCategories
andorderStatuses
succeeds. -
Challenge
Putting It All Together
Now it's time to take what you've learned in the previous steps and apply it to an application.
Start by clicking the
Run
button in the bottom right.Then, in the
Web Browser
tab, refresh the browser and you'll see a store front for Carved Rock Fitness.Notice that there is a
My Orders
menu item. This item will show you the orders that you have in the system. But in order to get the orders to show up, you need to fetch some data.Start by opening the file
store/modules/orders
.This contains a function where you'll be writing your code.
You'll notice at the top of the function, there are promises for every piece of data that you will need.
let statuses = axios.get("http://localhost:8888/orderStatuses"); let users = axios.get("http://localhost:8888/users"); let items = axios.get("http://localhost:8888/items"); let addresses = axios.get("http://localhost:8888/addresses"); let orders = axios.get('http://localhost:8888/orders?userId=2');
Notice that the call to
orders
specifies theuserId
. It's hard-coded to 2 for this example, but in a real world application, it would use the id of whatever user is logged in.It's important to note that this is not a good way to load data in a real application. It works in this situation because there is not much data and so connecting the data on the client will not have a performance impact. In a more real-world application, a request to
orders
would likely return the status, items and addresses as one object.To make the orders show up the following steps need to happen:
- Queue up all promises using
Promise.all
- Set the metadata, such as
statuses
on themetadata
object. - Create an array of orders that contains the
status
,address
, and list ofitems
At this point, you've retrieved all of the metadata that you will need for the order, and you've passed theorder
data on to the nextthen
block. In theWeb Browser
tab, if you refresh and click theMy Orders
menu item, you should 2 cards, one for each order.
- Queue up all promises using
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.