1
votes

I'm working with clojurescript and localforage a promise based storage library. I have a structure set up like the following in localstorage as key value pairs

"names" : ["name1","name2","name3"]

Where then each "names" is a key for another set of values.

"name1": [val1,val2,val3]

I'm currently at the point where I have the list of names and now need to iterate through that list, get the values for each one, and then return a map with a format like:

{:name1 [val1,val2,val3] :name2 [val1,val2]}

To accomplish this, I came up with the following snippet:

(defn get-project-dates [project-map]
  "Handles getting all the times/dates for project"
  (loop [i 0
        project-dates {}]
    (if (= i (count project-map))
      project-dates
      (.then (.getItem localforage (nth project-map i)) (fn [promiseVal]
          (recur (inc i) (conj project-dates {(key (nth project-map i)) promiseVale})))))))

Unfortunately this doesn't work as instead of recur going to the loop, it will go back to the (fn). This (fn) callback is however required as the (.getItem) call returns a promise that I can't access otherwise.

My question is then is there a way to get that promise value out and recur to the loop, or a better way overall to do this?

1
isn't it better and more idiomatic (in terms of both cljs and js) to use Promise.all? something like: (.then (Promise/all (map #(.then (.getItem localforage %) (fn [v] [% v])) project-map)) (partial into {})) - leetwinski

1 Answers

1
votes

There is no way that I know of to pick what you want to recur into. As far as I know, it's entirely dependant on scope.

You could make it a straight recursive function though. There's a few ways you could set this up:

; Give it two argument lists. The 1-arity version is meant to be called by the user, 
;  while the 3-arity version is meant for recursive calls 
(defn get-project-dates
  ([project-map i project-dates]
   (if (= i (count project-map))
     project-dates
     (.then (.getItem localforage (nth project-map i))
            (fn [promiseVal]
              (get-project-dates
                project-map
                (inc i)
                (conj project-dates {(key (nth project-map i)) promiseVale}))))))

  ([project-map]
   (get-project-dates project-map 0 {})))

or

(defn get-project-dates [project-map]
  ; Define a local recursive function called "rec" (or whatever)
  (letfn [(rec [i project-dates]
            (if (= i (count project-map))
              project-dates
              (.then (.getItem localforage (nth project-map i))
                     (fn [promiseVal]
                       (rec
                         (inc i)
                         (conj project-dates {(key (nth project-map i)) promiseVale}))))))]

    ; Then start the recursion off
    (rec 0 {})))

The second has the benefit of not needing to pass project-map constantly, as well as a more succinct function name.

Of course, you need to be cautious if you may have an excessive amount of recursive calls. You'll need to test to see if a lack of recur is safe.