An elegant form of “do notation” for promises in 24 lines of JS
Because I don’t want to bloat my projects with huge libraries I only use partially (at least until tree-shaking has become mainstream), and also because I’m on a quest to try and find out which of the functional programming combinators are the most needed and most fundamental, I keep rewriting my own functional programming utility functions time and time again.
In summary, I do not do “npm -S install underscore” (or lodash) anymore, instead I write a file that I usually call “functional.js” and put inside “lib”. It usually never contains more than 200 lines at the end of a project, and it’s rock-solid unit-tested so all in all it doesn’t cost much, neither in time nor risk for the project.
Once I’ve written my functional.js file a few thousand times, I think I’ll have a pretty good idea of what’s really fundamental and I’ll extract it to yet another underscore-ish npm package.
Today I want to talk about what I call “chain”.
It’s like “compose” but it works in the direction that feels natural, i.e. it composes functions from left to right. I call it “chain” because “compose” is already taken by redux (which I use extensively) and redux’s “compose” works from right to left (which feels more natural for the usual redux use-cases such as composing component decorators).
The most basic version of my “chain” is this:
There are already a few noteworthy things here:
- It’s a bloody small function (so, do you really need undescore for this?).
- It accepts the functions first and then the initial input of the chain, because the actual initial value is a dirty runtime detail. Having it this way lets you write a lot of point-free code.
- It accepts the functions as a variable length list of arguments, because when you use the function, you’re usually explicitly passing arguments, not an array of previously defined functions.
Then I thought, it would be nice if I could chain promise-returning functions, too:
Basically it’s the same as before, except instead of calling the function, I check if the previous result is a thenable and “then” (pun intended) call the function via the “then” method of the previous result if it has one.
The bit about extracting the then and not doing “result.then” is because the Promises/A+ spec tells you you should only read “then” once when you do this sort of stuff. I know it because I’ve written a Promises/A+ implementation in 97 lines for fun.
Then I stumbled upon a real-life scenario where I needed to access the intermediate values from the middle of the chain.
Here’s the code (it’s in a test) I want to write for my real-world customer-facing software:
Why this test? Because I needed a method that generates unique ids under a specified scope, and the spec says that the ids should increment. Obviously “storage” speaks with a database, so it works asynchronously, and since I want to check that the ids do in fact increment, I need to generate them one after another for the assertion to make sense.
It’s usually a rather painful thing to do without “chain”, here’s what I had before:
Isn’t it awful? Aren’t promises supposed to solve the callback hell?
Well not by themselves. What you need is what is called “do notation”.
In JS we don’t have the beautiful syntax sugar that Haskell provides, but as you can see, we can still write something rather nice and intuitive.
Look again:
In haskell it would probably look something like:
So what we have in JS is not that worse!
Is it hard to do? No:
I basically just keep track of the collected results while I reduce the functions, and when I’m “reducing a promise”, I wait for the previously collected results to settle before passing them on. Since the thing to do on each function has become bigger, it is extracted to its own function called “chainStep”.
You can use this to do fun things like a maybe monad:
I’m stopping here, but the amount of interesting things you can do with this simple 24 lines long “chain” function is astonishing.
Can you see the point of writing one’s own utility functions now? It’s an opportunity to discover very useful patterns and improve your code. There’s no one-size fits all, you can spend a little time re-inventing a few wheels and actually get huge benefits out of it.
Next time, I think I’ll talk about “prop”, which lets you treat arbitrarily deep regular JS structures as immutable, manipulate deep properties very easily, and makes immutable code actually easier to read than the imperative equivalent. This one is a huge function of 34 lines…