Promises, Promises Pt. 3
Composing in ES6
I don’t know what your familiarity level is with functional programming, and I’m not planning to go too far into it. I’m currently taking a specialization on the subject on Coursera, but if you’re totally new to the concept, I would really recommend the YouTube channel Fun Fun Function by Mattias Petter Johansson (a.k.a. mpj). He breaks down the basics of functional programming reeeeeeaally well. For now we’re going to do what functional programming does best: compose.
Promises are a little like JavaScript Arrays in some ways. I mean, they’re both structures that wrap around values, and they both have a lot of what we call “higher order functions” that take in smaller functions and produce new structures. There’s a couple of functions that it helps to be familiar with: forEach
and map
. forEach
iterates through the array and does something with each value.
This is very imperative, very performative. Take the value, do something with it. map
is little more functional, more informative. it takes in a transformer function that transforms a value into a different kind of value, and returns a new array with all the values passed through that transformer function.
In this case, I wasn’t super transparent about the then
and catch
functions. In the last post we treated them like the forEach
method of JavaScript Arrays, i.e., just use it to access the value and do something with it. But then
can also act like the Array’s map
function, transforming the promise. Take a look below:
So we can use then
like a map
function! What about catch
? Can we transform the error? Let’s take our luckyPromise
from before and switch it, so that when it has a value, that becomes the error, and when it gets an error, that’s valid.
But wait, there’s more! There’s a third function that then
acts similar to: flatMap
. Now, I do wish to the gods that JavaScript arrays had the flatMap
function. Seriously, I don’t know why it’s not there. But the idea is that flatMap
would take in a function that takes in one of the array values and return a new array. Then flatMap
would return a new array that concatenated all those values together. Instead of returning an array of arrays, it just returns an array of values.
It may seem a little weird to talk about a function that doesn’t exist, but it does in lots of other libraries like this. In fact, it’s a pretty integral part of the definition of a monad which is this big thing in the functional world. Most other languages with functional collection wrangling have it, like LINQ (though they call it SelectMany
). Anyway, my point is this: if Promises had a function like that, it would take in a function that returns a promise, but instead of returning a promise of a promise of a value, it just returns a promise of that value. Well, then
and catch
do this implicitly! If the return value of the function is a promise, it just shortens that down.
That’s right! Your first instinct is that composedVal
should be the internalPromise
, since that’s what’s returned from that function, but promises see that you’re returning a promise and compose a flatMap
instead of a regular map
. One of the great parts of that is that you can use this to compose promises, or even provide a fallback promise.
Combining Promises
We can see how to use then
to perform some of this combining. There are two more functions from the Promise API that I want to point out: Promise.all
and Promise.race
.
All
Promise.all
is a great function that turns an Array of promises into a promise of an Array. It resolves when all of those promises have resolved, or rejects as soon as any of them rejects. Let’s take my nacho example and say that I have sent out two roommates, one for chips and one for cheese.
Race
I will be honest that I have not used this one in production yet, but Promise.race
is the “logical or” to Promise.all
’s “logical and”. If all the items reject, this will reject with an array of their reasons, but if any of the values resolves, this will resolve.
I usually found that, if I requested the promise, I probably need it, but this might be a good way of prioritizing content based on when it came back while waiting for the other stuff to show.
Summary
So, this concludes the ES6 Edition of Promises. In the following post, I’ll do the same thing, but for Angular.js syntax, then we’ll talk about some real world problems I had and how I composed promises to solve them.