Promises in JavaScript
Megan Lo
Posted on June 20, 2021
Hope you had a great break before continuing the series!
In this article, we would cover Promises
. If you haven't read the previous article (Intro to Asynchronous JS), I highly recommend you to first read it before coming back to this article, as it builds an important foundation for this article.
There are 4 parts in this series:
- Intro to Asynchronous JS
-
Promises
(this article) - More
Promises
async/await
Introduction
Promises
was introduced in ES6 to simplify asynchronous programming.
I would divide this article into the following sections:
- Why was
Promises
introduced? (Spoiler Alert: Trouble with callbacks) - Promise Terminology
- Basic Promise usage
- Promise Consumer:
then
,catch
,finally
In the next article, we'll cover:
- Chaining Promise
- Fulfilling multiple Promises
Before Promises: Old-style Callbacks
Before the introduction of Promises
in ES6, asynchronous was commonly handled with callbacks (calling a function within another function). This is important to know before diving into Promises
.
Let's see some callback example. Imagine you are ordering Starbucks coffee on a Monday morning and you are feeling cranky. Unfortunately, you don't just get your coffee with a snap.
You have to first decide what kind of coffee you want, then you place your order with the barista, then you get your coffee, last but not least,
Here's how the callback's going to look like (reference to MDN doc on Promise):
chooseCoffee(function(order) {
placeOrder(order, function(coffee) {
drinkCoffee(coffee);
}, failureCallback);
}, failureCallback);
As you can see, it's very messy-looking! This is what is often referred to as "callback-hell". Promises
allow these kinds of nested callbacks to be re-expressed as Promise chain, which we would cover more in the next article.
In the following section, we would first cover the terminology, then we'll dive into the basic Promise usage using the callback functions we saw in the series.
Promise Terminology
Here's the basic syntax of Promise
:
let promise = new Promise(function(resolve, reject) {
// executor
});
The arguments resolve
and reject
are the two callbacks provided by JavaScript.
Here are the three states in a promise you need to know:
- pending: when a promise is created, it is neither in success or failure state.
- resolved: when a promise returns, it is said to be resolved.
-
fulfilled: when a promise is successfully resolved. It returns a value, which can be accessed by chaining a
.then
block onto the end of the promise chain (will discuss this later in the article) -
rejected: when a promise is unsuccessfully resolved. It returns a reason, an error message why it is rejected (
Error: Error here
). This can be accessed by chaining a.catch
block onto the end of the promise chain.
Here's a more visual graph from javascript.info
Basic Promise usage
In a promise, there can be only one result or error. So let's say we have this Promise function:
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("Not okay!")); // ignored
})
The result will immediately show "done" and the error will be ignored.
This is the easy version. This is what you can expect how a promise will look like:
let example = () => {
return new Promise(function(resolve, reject) => {
let value = function1();
if (job success) {
resolve(value);
} else (job unsuccessful) {
reject(console.log("something's wrong!! :("));
}
}).then(function(value)) {
// success
return nextFunction(value);
}).catch(rejectFunction);
}
Okay, it's getting a lot and we've got some new friends in the above function. What's .then
and .catch
? We will get to them in the next section, but just a quick breakdown from above:
- As a promise is created, if the function is passed successfully, the promise will be resolved.
- On the other hand, if the function is passed unsuccessfully, the promise will be rejected and "something's wrong!! :(" will be printed on the console.
This is all you need to know for now! Let's move to our consumers in Promises
!
Consumers: .then
, .catch
, .finally
A Promise object serves as a connection between the executor (you know the resolve
, reject
) and the consuming functions. Consuming functions can be registered with methods: then
, catch
, finally
(which you've already seen .then
and .catch
in the previous section!).
Here's the promise cycle:
1οΈβ£ A promise is created (State: Pending)
2οΈβ£ a
ππ» A promise is resolved (State: resolved)
ππ» Promise chain with .then
2οΈβ£ b
ππ» A promise is rejected (State: rejected)
ππ» .catch
to handle errors
3οΈβ£ .finally
to give the final result of the promise π―
.then
As I learned how to use these consumer methods, I like to think .then
as... then what would you like to do after we resolved the promise?
Consider .then
is similar to AddEventListener()
. It doesn't run until an event occurs (i.e. the promise is resolved).
let promise = new Promise(function(resolve, reject) {
setTimeOut(() => resolve("done!!"), 1000);
});
promise.then(
// shows "done!" in console after 1 second
result => console.log(result)
);
Note: You can show errors using .then
.
.catch
: Error Handling
Promises is not always resolved, but there are cases where promises is rejected. Therefore .catch
is here to catch errors.
Here's how we remember:
-
.then
works when a promise is resolved. -
.catch
works when a promise is rejected.
If we are interested in seeing errors, here's how we use .catch
:
let promise = new Promise(function(resolve, reject) {
setTimeOut(() => reject(new Error("NO!")), 1000);
});
// shows the error after 1 second
promise.catch(result => console.log(result));
Feel free to copy the code above to your terminal/Chrome DevTool (if you are using Chrome). You should see the following:
Note: .then(null, func)
is the same as .catch(func)
.finally
.finally
, which is introduced in ES2018, is like a decent closure for the promise. Think of it like finally we are done and it's time to disclose the final result. In other words, it works no matter the promise is resolved or rejected.
.finally
is a good handler for performing cleanup, like stopping the loading indicator.
If a promise is resolved:
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 2000);
})
promise.finally(() => console.log("Promise ready"));
promise.then(result => console.log(result));
If a promise is rejected:
let promise = new Promise((resolve, reject) => {
throw new Error("error");
})
promise.finally(() => console.log("Promise ready"))
promise.catch(err => console.log(err));
Well let's put all these together, shall we?
Let's apply our new knowledge to our Monday morning coffee from the callback-hell section, but I will add one more condition is that we will only buy coffee if our mood rates lower than or equal to 5 (out of 10):
function orderCoffee() {
return new Promise((resolve, reject) => {
let rating = Math.random() * 10;
// this is only a reference so that
// we know what the rate of mood is
console.log(rating);
if (rating > 5) {
resolve("I AM FEELING GREAT!");
} else {
reject(new Error("We are going to Starbucks..."));
}
});
}
orderCoffee()
.then(mood => console.log(mood))
.catch(err => console.log(err))
.finally(() => console.log("Decision's been made!"));
(Code reference from MDN's Promise.prototype.finally())
If the mood rates higher than 5 (i.e. the promise is resolved) (You can see the number on the first line after the handlers):
If the mood rates lower than or equal to 5 (i.e. the promise is rejected):
Feel free to copy the code above on your ChromeDevTool/terminal to play around!!
A quick recap on this section:
- If a promise is resolved,
.then
will take over the rest. - If a promise is rejected,
.catch
will take over and return the error. -
.finally
is good for performing cleanup as it works no matter the promise is resolved and rejected.
Alright, these are the basics of Promises! In the next article, we'll talk more about chaining promises and fetching multiple promises!
Here's a quick glimpse using Promise chain from our callback example:
chooseCoffee()
.then(order => placeOrder(order))
.then(coffee => drinkCoffee(coffee))
.catch(failureCallback);
Resources:
π Highly Recommend: Promise (javascript.info)
π Eloquent JavaScript Chapter 11: Asynchronous Programming
π JavaScript The Definitive Guide by David Flanagan (7th Edition) Chapter 13.2: Promises (Pg. 346 - 367) (Amazon)
π Graceful asynchronous programming with Promises (MDN)
π JavaScript Async/Await Tutorial β Learn Callbacks, Promises, and Async/Await in JS by Making Ice Cream π§π¨π¦ (FreeCodeCamp)
π How To Implement Promises in JavaScript?
π¦ If you are looking for more explanation (or a different way of explaining) on this concept, I'd like to recommend my friend, Arpita Pandya's article:
JavaScript Promises
Posted on June 20, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.