My Promises Crash Course
I’ve been doing a lot of front end work lately! It’s been a fun experience, especially since the typical front end technologies aren’t something I was exposed to in a major way during school. Today I needed to write a function that required me to actually learn how Javascript Promises work, as well as how to aggregate Promises into a single Promise. So, heres a summary of the crash course I had to take.
Whats a promise?
From the MDN Docs:
The
Promise
object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
This comes up if you want to query an external API: retrieving data over the network is asynchronous. Javascript’s Fetch API
uses Promises to provide the data from requests once it becomes available. Not only that, but the async/await
pattern that was introduced in ES2017
makes use of Promises under the hood. Use that syntax to avoid 👹callback hell👹.
A promise is really just any object or function that implements a .then()
method. That .then()
method needs to accept two arguments: onFulfilled
and onRejected
.
So, our mental model so far is:
If you’re unfamiliar with Javascript or this syntax, here is a good resource
onFulfilled
and onRejected
need to behave in specific ways. onFulfilled
needs satisfy:
- is called when the Promise is Fulfilled
- is called exactly once
- is NOT called if the Promise is not Fulfilled
onRejected
is the same, except we call it when the Promise is rejected. These functions are provided by whoever is creating the promise. This means we can supply the
success / error behavior the promise should have in the form of callbacks. then()
has access to the current state of the promise, which means a promise has to keep some
internal state with transitions. This diagram shows what state machine
this object has to implement. So, then()
looks something like:
We could go down a rabbit hole of expanding this mental model, but here’s a great article walking through what a Promise implementation would look like. We have enough now to see how we could aggregate promises.
Aggregating promises
Here’s the situation I was in. I was writing a function that was restricted to returning something with a type of Promise<DataType[]>
. However, the only way I could
get access to any elements of DataType
was a function that returned Promise<DataType>
. In code:
Luckily, the Promises API has a function that can perform this aggregation: Promise.all()
.
Given an iterable of Promises as input, we get back a single promise that will resolve to the array of results. So, we can call our first function to get individual data items from our API,
then package all of those promises into a single promise. The great part is that performance wise this isn’t even that bad: as soon as we call our first function, thats it! It spits back a
promise that will eventually resolve, and each of those requests aren’t blocking each other. We are doing them in parallel! That looks something like this:
Pretty neat! The aggregate promise will reject if any of the individual requests reject, and will resolve when every one of the promises have been fulfilled. The promise API
also has some other functions such as .any()
returning the first promise that resolves, race()
which resolves when any of the provided promises resolve, and
allSettled()
which returns a promise that resolves to an array containing the promises result (reject or resolve).
Extra fun fact, Promises
aren’t supported on internet explorer! ☠️ RIP ☠️.