Skip to content

Instantly share code, notes, and snippets.

@latant
Last active October 20, 2021 21:56
Show Gist options
  • Save latant/b3d0877006a274f3122b57352d8ec28b to your computer and use it in GitHub Desktop.
Save latant/b3d0877006a274f3122b57352d8ec28b to your computer and use it in GitHub Desktop.
Clojure async/await for CompletableFuture using cloroutine. It works with thread bindings.
(ns async
(:require [cloroutine.core])
(:import (java.util.concurrent CompletableFuture)
(java.util.function BiConsumer)
(clojure.lang Var)))
(def ^:dynamic *coroutine*)
(def ^:dynamic *value*)
(def ^:dynamic *error*)
(defmacro with-binding-frame [f & body]
`(let [f# (Var/getThreadBindingFrame)]
(Var/resetThreadBindingFrame ~f)
(try ~@body
(finally (Var/resetThreadBindingFrame f#)))))
(defn <f [^CompletableFuture cf]
(let [bf (Var/cloneThreadBindingFrame)]
(.whenComplete cf
(reify BiConsumer
(accept [_ v e]
(with-binding-frame bf
(binding [*value* v, *error* e]
(*coroutine*))))))))
(defn call-back []
(if-some [e *error*] (throw e) *value*))
(defn start-coroutine [cr]
(binding [*coroutine* cr] (cr)))
(defmacro f> [& body]
(let [cf (with-meta (gensym "cf") {:tag `CompletableFuture})]
`(let [~cf (CompletableFuture.)]
(start-coroutine
(cloroutine.core/cr {<f call-back}
(try (.complete ~cf (do ~@body))
(catch Throwable e#
(.completeExceptionally ~cf e#)))))
~cf)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment