Last active
April 23, 2026 15:02
-
-
Save latant/57eb02ccad29e4e0d626f87d6665fa11 to your computer and use it in GitHub Desktop.
ClojureScript async/await macros via cloroutine
This file contains hidden or 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
| ;; async/await macros for ClojureScript via cloroutine | |
| ;; Requires: cloroutine "13" (shadow-cljs.edn :dependencies) | |
| ;; | |
| ;; Usage: | |
| ;; (ns my.ns | |
| ;; (:require [async-await]) | |
| ;; (:require-macros [async-await :refer [async await]])) | |
| ;; | |
| ;; (async | |
| ;; (let [data (await (fetch-something))] | |
| ;; (println data))) | |
| ;; | |
| ;; NOTE: Use (.method js/Obj args) instead of (js/Obj.method args) inside | |
| ;; async blocks. See https://github.com/leonoel/cloroutine/issues/25 | |
| (ns async-await | |
| #?(:cljs (:require [cloroutine.impl])) | |
| #?(:cljs (:require-macros [cloroutine.core :refer [cr]] | |
| [async-await]))) | |
| ;; --- ClojureScript runtime --- | |
| #?(:cljs | |
| (do | |
| (def ^:dynamic *fiber* nil) | |
| (def ^:dynamic *value* nil) | |
| (def ^:dynamic *error* nil) | |
| (defn -await [p] | |
| (let [fiber *fiber*] | |
| (.then p | |
| (fn [v] (fiber v nil)) | |
| (fn [e] (fiber nil e))))) | |
| (defn thunk [] | |
| (if-some [e *error*] (throw e) *value*)))) | |
| ;; --- Macros (runs on JVM / Clojure side) --- | |
| (defmacro await | |
| "Suspends the current async coroutine until the JS Promise p resolves. | |
| Returns the resolved value, or throws on rejection. | |
| Must be used inside an async block." | |
| [p] | |
| `(-await ~p)) | |
| (defmacro async | |
| "Wraps body in a JS Promise backed by a cloroutine coroutine. | |
| Inside the body you can use (await <promise>) to park without blocking. | |
| WARNING: Due to a known cloroutine bug (https://github.com/leonoel/cloroutine/issues/25), | |
| avoid (js/Foo.method args...) call syntax inside async blocks. Use (.method js/Foo args...) | |
| instead. This includes indirect call positions such as threading macros: | |
| Bad: (await (js/Promise.resolve x)) | |
| Good: (await (.resolve js/Promise x))" | |
| [& body] | |
| `(js/Promise. | |
| (fn [resolve# reject#] | |
| (let [fiber-ref# (volatile! nil) | |
| cr# (cloroutine.core/cr {async-await/-await async-await/thunk} | |
| (try (resolve# (do ~@body)) | |
| (catch :default e# | |
| (reject# e#))))] | |
| (vreset! fiber-ref# | |
| (fn [v# e#] | |
| (binding [async-await/*fiber* @fiber-ref# | |
| async-await/*value* v# | |
| async-await/*error* e#] | |
| (cr#)))) | |
| (binding [async-await/*fiber* @fiber-ref#] | |
| (cr#)))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment