Last active
August 29, 2015 14:10
-
-
Save vvvvalvalval/005eaef8986e20f37d0d to your computer and use it in GitHub Desktop.
Hack for attaching asynchronous callbacks to Clojure promises
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(require '[clojure.core.async :as a]) | |
(defn make-promise-listener! | |
"Creates a function that attaches handlers to Clojure promises. | |
Behind the scences, fires a loop in another logical thread that scans every refresh-period ms the promises that have been registered for completion, | |
and executes the each handler on the corresponding value." | |
[{:keys [refresh-period] | |
:or {refresh-period 10}}] | |
(let [next-index! (let [a (atom -1)] #(swap! a inc)) ;; to get unique keys to put in the map. | |
state (atom {})] ;; map of dummy unique keys to pending promise-handler pairs | |
(a/go-loop [] | |
(let [period-to (a/timeout refresh-period)] | |
(doseq [[i {:keys [prom handle!]}] (filter #(-> % second :prom realized?) @state)] | |
;; for each promise that has been delivered, fires the handler function in another thread | |
(swap! state dissoc i) ;; when realized, forget about the promise | |
(a/thread (handle! @prom))) ;; trigger callback in another thread | |
;; refreshes every refresh-period | |
(a/<! period-to) (recur) | |
)) | |
(fn then! [p handle-p!] | |
(if (and (instance? clojure.lang.IDeref p) | |
(instance? clojure.lang.IPending p) | |
(ifn? handle-p!)) | |
(let [i (next-index!), pending {:prom p :handle! handle-p!}] | |
(swap! state assoc i pending) | |
nil) ;; return nil so state stays hidden | |
(throw (RuntimeException. "Wrong params"))) | |
))) | |
(def then! (make-promise-listener! {})) ;; global promise listener | |
(comment ;; example | |
(def my-promise (promise)) | |
(then! my-promise println) | |
(deliver my-promise "Hello there!") ;; "Hello there!" gets printed | |
) | |
;; core.async channel wrapper | |
(defn as-chan [p] | |
(let [c (a/chan 1)] | |
(then! p #(do (a/>!! c %) (a/close! c))) | |
c)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment