How would you program async filtering of a list?


#1

Here’s a simplified version of my question. The code assumes there is not async code. Say we have a list with 2 hierarchies, parents and children, parents have “name”, children have “value”, I want to filter the list by:

  • parent with name greater than “c”,
  • child with an odd value,
  • parent should not be empty.

Or with code:

(ns app.main )

(defn filter-child [x] (odd? (:value x)))

(defn filter-parent [x] (pos? (compare (:name x) "c")))

(def list-data
  [{:name "a", :children [{:value 100}]}
   {:name "b", :children [{:value 22}]}
   {:name "c", :children [{:value 3.2}]}
   {:name "d", :children [{:value 4}]}
   {:name "e", :children [{:value 50} {:value 51} {:value 52} {:value 53}]}])

(defn task! []
  (println
   (pr-str
    (->> list-data
         (filter filter-parent)
         (map (fn [x] (update x :children (fn [ys] (->> ys (filter filter-child) vec)))))
         (filter (fn [x] (not (empty? (:children x)))))
         (vec)))))

(defn main! [] (println "Started.") (task!))

(defn reload! [] (.clear js/console) (println "Reloaded.") (task!))

It prints values:

[{:name "e", :children [{:value 51} {:value 53}]}]

Now the question is, if bother filter-parent and filter-child are async function, say behind a network request, how would do implement the program, in an elegant way? What would you choose, core.async, or Promise? What if it’s nested with more hierarchies?


#2

I might be missing something, but I’d say this would be a straightforward use of core.async.
You’d send data to your api endpoints and block/park on the response. The api call might possibly spin a separate thread/go-process and write the response to a channel.


#3

Promises might be enough, in the end it’s up to you. If you go for promises Promesa seems pretty nice and is strait foreword http://funcool.github.io/promesa/latest/#introduction

A nice talk on the topic of promises vs core.async


#4

The question is about ClojureScript where blocking is not possible.

IMHO you are asking the wrong question. It doesn’t matter which async “mechanism” you choose for this since none of them are going to look anywhere near what the sequential version looks like. It’s also quite impossible to answer this question without knowing more about the async parts. Can they run concurrently or will they interfere with each other? Do they need to complete in-order? What about failure? Can the program complete if one “answer” is missing? What about timeouts? There are a whole lot more things you need to think about and which framework to use is dependent on previous answers.


#5

True. But still there’s a question about how we write async programs with ClojureScript. I want to start with the simpler cases where we don’t think about error handling and no orders or dependencies are interfered.


#6

I tend to reach for bluebirdjs whenever I’m forced to do something like async iteration. You can get a lot done with bluebird’s map function, which has concurrency control, and Promise.all. The concurrency is really what kills you because if you don’t watch it you will start n simultaneous network calls, which will crush the endpoint and and often cause timeouts if run on a browser.