Promises
Last updated
Last updated
Table of Contents
Synchronous operations MUST be completed before the program can do anything else. Most code we write is synchronous (and that’s okay because most things take very little time to complete).
An asynchronous operation is anything that takes time to complete.
Imagine walking into a Pizza shop and you ask for a slice of cheese pizza. The pizza isn’t ready yet so you have to wait. The person at the register gives you a ticket to claim your slice when it is ready. Meanwhile, you are free to run other errands and can return later to pick up your pizza. You get notified that the pizza is done so you return to the shop, hand in your ticket, and take your pizza home.
This is the essence of asynchronous operations.
Some operation is going to take some time to complete
In the meantime, you can do other things.
When the operation is complete, we can do something in response.
When working with asynchronous code, since we aren’t always aware of when the operation will complete, we need to provide instructions for what to do when that time comes.
We’ve done this with event handlers using a callback: when a button is clicked, handle it with this callback...
With setTimeout
we also do this with a callback. setTimeout
is a good way to fake asynchronous operations. setTimeout
takes a callback and an amount of time in milliseconds (1000ms === 1s). It will execute the callback after the time passes.
Synchronous operations will complete first, then asynchronous operations, no matter how long the async operation takes.
In the previous example, we essentially start all of the timers at the same time. We aren't waiting for one to finish to start the next. But what if we wanted to?
The basic idea behind this approach is:
Start an asynchronous process
When the operation is complete, do something next…
When that operation is complete, do something next…
When that operation is complete, do something next…
This approach is commonly referred to as callback hell.
Promises aim to provide a more readable approach.
Promises are used when we want to get some data, but we know it will take time. More specifically, a promise is an object that represents the eventual completion of an asynchronous operation and the resulting value.
For example, if we want to get data about the current weather from a weather API, it may take time, especially if our internet is slow. When we make that request, we'll be given a promise that will eventually "resolve" to the weather data.
Using a Promise involves two steps:
Start the asynchronous operation and get a Promise back (order your pizza, get your ticket)
Define how to handle the resolved/rejected Promise using .then()
and .catch()
(when ready, I will hand in my ticket and get my pizza)
As the asynchronous operation is carried out, the Promise will exist in one of three states:
pending - the operation is still working and the promise is not yet “settled”
fulfilled - the operation was a success! We got the value we wanted. Commonly referred to as “resolved”
rejected - the operation failed. We got an error back.
When making a Promise, we define when/how it will resolve.
Then, the “consumer” of the Promise defines what to do with the resulting value.
When making a Promise we call resolve(...)
when the asynchronous operation succeeds and reject(...)
when it fails.
In this example, we use setTimeout(...)
to simulate async code. A more realistic example might make an HTTP request or interact with a database, something that takes time.
Most often, you don’t create Promises yourself. You’ll use functions like fetch
that return a Promise and you’ll just "consume" the Promise. You will need to know how to use the returned Promise (using .then
), but it’s good to know how a Promise is made.
In the last example, the Promise always resolves. But many Promises can also reject, often if an error occurs.
In this next example, the Promise will randomly resolve or reject.
The “consumer” of the Promise then handles each case accordingly.
In the last example, we invoke resolve
and reject
with the same value. However, as a best practice, we should reject
with an Error
object. Most producers of Promises adhere to this best practice and most consumers of Promises expect this.
Now this is where Promises are super useful and solve the callback hell approach.
.then()
(and .catch()
) will always return a Promise that you can chain additional .then()
calls to.
The Promise that .then()
returns will resolve to the value that its callback returns.
Next time, we’ll write code like this: