How to avoid SQLite callbacks with Core.async?

To follow up, I’ve had some success. Now, my functions are much more isolated in what they do, the inputs are clearer, and there are no callbacks.

First, as per your advice, I abstracted out a generic sql function. I think it could be better still, but I’ve set it up so that depending on the type of call it will return different things:

(defn <sql
  "Creates a sql operation that returns a channel, allowsing for async/await like syntax.
  Has been abstracted to handle variety of return types depending on sql op(eration)"
  [{:keys [sql params op]}]
  (let [out    (promise-chan)
        params (apply array params) ;; TODO - if this is not a sequence, handle it?
        cb     (fn [err res]
                 (this-as this
                   (if err
                     (put! out (ex-info (str "Failed to run async query of type " (name op)) {:error :sql-error :res res}))
                     ;; TODO nil - nothing coming back.
                     (cond
                       (= :insert op) (put! out (.-lastID this))
                       res            (put! out (js->clj res :keywordize-keys true))
                       :else          (put! out (js->clj this :keywordize-keys true))))))]

    (case op
      :all    (.all db sql params cb)
      :get    (.get db sql params cb)
      :insert (.run db sql params cb)
      :run    (.run db sql params cb))
    out))

Now I can call functions like articles-get as <articles-get:

(defn <articles-get
  []
  (<sql {:op :all :sql "SELECT * FROM articles ORDER BY date_created DESC"}))

or

(defn <insert-article
  "Creates a new article. Requirements:
  -> words for article are already in words table.
  -> words from article have been re-queries
  -> requiriesed words' ids have been made into a delimited string with $."
  [{:keys [article title source word_ids]}]
  (<sql {:op     :insert
         :params [article word_ids title source (js/Date.now)]
         :sql "INSERT INTO articles(original, word_ids, name, source, date_created) VALUES (?, ?, ?, ?, ?)"}))

Which can be called like so, from the Ipc handlers:

(def ipcHandlers
  {(s-ev :article-create)
   (fn [event data]
     (go (let [x                (<! (db/<insert-words (data :article)))
               word-ids-str     (<! (db/<get-word-ids (data :article)))
               inserted-article (<! (db/<insert-article (merge data {:word_ids word-ids-str})))]
           (reply! event (s-ev :article-created) inserted-article))))
;...

Anyway - I have yet to discover if I’m creating future foot guns for myself, but this feels a lot cleaner to write.

Thanks for your help!

1 Like