Skip to content

Instantly share code, notes, and snippets.

@iku000888
Last active May 18, 2017 15:22
Show Gist options
  • Save iku000888/443510d3235e94359c26b0c97fe8f45f to your computer and use it in GitHub Desktop.
Save iku000888/443510d3235e94359c26b0c97fe8f45f to your computer and use it in GitHub Desktop.
Some fun with callbacks
;; Given a seq of something and an async fn that accepts a callback,
;; how would you process the seq synchronously in order?
(def atm (atom []))
(reset! atm [])
(add-watch atm
:watcha
(fn [k r o n]
(prn k o n)))
;; Some async action that accepts a callback
(defn async-thing [callback]
(future (Thread/sleep 1000)
(callback)))
;; 'async', order of elements will likely be out of order
(doseq [el (range 10)]
(async-thing (fn [] (swap! atm #(conj % el)))))
;; Looks synchronous, but elements get processed in reverse order
((loop [[head nxt & left] (range 10)
head-clbk (fn []
(async-thing (fn [] (swap! atm #(conj % head)))))]
(if nxt
(recur `[~nxt ~@left] (fn [] (async-thing
(fn []
(head-clbk)
(swap! atm #(conj % nxt))))))
head-clbk)))
;; Switching nxt/head does not help!
;; Is there a way to process elements in order without reversing the seq?
;; Is core.async a tool that could be useful in this situation?
((loop [[head nxt & left] (range 10)
head-clbk (fn []
(async-thing (fn [] (swap! atm #(conj % nxt)))))]
(if nxt
(recur `[~nxt ~@left] (fn [] (async-thing
(fn []
;; Note switching order of these two does not
;; affect the result as (head-clbk) returns right away.
(head-clbk)
(swap! atm #(conj % head))))))
head-clbk)))
;; Using a channel with watches,
;; we can achieve one element at atime & preserve order
(def chan (atom #{}))
(reset! atm [])
(loop [[el next-el & left] (range 100)]
(add-watch chan
el ;;The element to watch for
(fn [k r o n]
;; Only want to fire the task when the
;; watching key has been newly conj'ed
(when (and (get n k) (not (get o k)))
(async-thing
(fn []
(prn "doing async task for..." k)
(swap! atm #(conj % el))
;; Triggers the watch for next-el
(swap! chan #(conj % next-el)))))))
(if next-el
(recur `[~next-el ~@left])
nil))
;; Add the first element, and the watches start to cascade
(swap! chan #(conj % 0))
@iku000888
Copy link
Author

And keeping order + async was something that I was curious, so thanks for that too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment