Promises
Table of Contents
Sync vs. Async
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).
console.log(1);
console.log(2);
console.log(3);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.
So… what next? Callbacks
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.
Limitations of Callbacks — Callback Hell
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.
So… what next? Promises!
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.
Promise Syntax: Resolve and Then
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 andreject(...)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
fetchthat 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.
Promise Syntax: Reject and Catch
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.
Best Practice: Reject with an Error
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.
Chaining Promises
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.
Coming up...
Next time, we’ll write code like this:
Last updated