Promises with core.async
Is there a
core.async
equivalent toPromise.all
?
One of the things I love about ClojureScript is that it feels like it provides the right building blocks. Only in rare cases does the language hinder me when I’m trying to solve a problem.
When I saw the above question, I knew there had to be either a built-in fn or a simple way to implement it. First, what does Promise.all
provide?
- Resolves promises concurrently
- Returns the values from the list of promises, in the order they were given
- Returns the value of a rejected promise immediately (i.e. does not wait for other promise resolutions)
For clarity in mentally mapping core.async to Promises, functions are named as if we were using Promises.
Concurrent resolution
With core.async
go
blocks, we can run code concurrently:
Returning values in order
core.async
doesn’t provide a built-in fn that both takes the value from each channel and returns those values in the order the channels were given. But writing a fn is simple enough:
In this fn, we just read from the first channel and store the value into our collection. Then, if there are more channels to read, we recur with the rest of the channels. Again, we return a channel with the collection as a value.
Rejecting Immediately
To reject immediately, we need to combine two concepts. First, we provide a channel to each “promise” that allows the promise to reject. For the promise to reject, it simply puts its value to the reject channel and cleans up as necessary.
Now, we need to get either the list of resolved values or the rejected value, whichever comes first. The fn alts!
lets us get the first available value from a list of channels:
We take our reject channel and list of channels (i.e. promises) and combine our channels using our into
fn. Then, we wait on either the reject value or the resolved values via alts!
. If the first value to come back is via the reject channel, we go into our rejection code. Otherwise, we have our ordered list of resolved values and handle them appropriately.
This was a fun little problem that reminded me of why I love cljs. With cljs, we don’t have to worry about which browsers implement Promise.all
. We have the right building blocks to do it ourselves. And core.async
is seriously awesome; just about every time I use it, it amazes me that this functionality is provided as a library!