(refer-global :only '[Promise console fetch]) ;; used in examples throughout this reference
(^:async fn [n]
(let [x (await (Promise/resolve 10))]
(+ n x)))
Since v1.12.145, hinting a function with ^:async makes the ClojureScript compiler emit a JavaScript async function.
Define an async function by placing :async metadata either on the fn symbol
or on the name of the function. Inside the body, await suspends the function
until the Promise resolves and yields its value.
(refer-global :only '[Promise console fetch]) ;; used in examples throughout this reference
(^:async fn [n]
(let [x (await (Promise/resolve 10))]
(+ n x)))
(defn ^:async foo [n]
(let [x (await (Promise/resolve 10))]
(+ n x)))
The only valid placement of ^:async metadata is on the fn symbol or on the name of a function.
These placements are invalid:
(defn foo ^:async [urls]
,,,)
^:async (fn [urls] ,,,)
The await macro may be used anywhere in an :async function body, including
inside try/catch. This makes error handling follow the same shape as non-async
code.
(defn ^:async fetch-json [url]
(try
(let [resp (await (fetch url))]
(await (.json resp)))
(catch :default e
(.log console "fetch failed" e)
nil)))
Note that even though the await call is in return position, it is necessary here to
handle the rejection within the scope of the try/catch. Without it, the
rejection would have to be handled by the caller of fetch-json.
The cljs.test deftest macro supports :async on the name of the test.
(require '[cljs.test :refer [deftest is]])
(deftest ^:async my-test
(let [v (await (foo 10))]
(is (= 20 v))))
The await macro may only be called inside an :async function and the return value of the function will always be a JS Promise.
CLJS does not support top-level await.
Multi-arity functions cannot be mixed sync and async. This is why CLJS doesn’t let you put :async on the arg vector.
The await macro in CLJS has nothing to do with the await function in JVM Clojure. CLJS does not have agents.
Functions created inside an :async function are non-async unless also annotated with :async metadata. So the following won’t work:
(defn ^:async fetch-statuses [urls]
(map (fn [url] (.-status (await (fetch url)))) urls))
In this case the compiler will throw an error with the message "Assert failed: await can only be used in async contexts".
Marking the inner fn ^:async will compile, but map is synchronous and now
returns a seq of Promise values. The outer fn, fetch-statuses
is now needlessly :async, since no await is even used inside it
directly. The return value of this function will be a Promise resolving to a seq of
Promise values.
(defn ^:async fetch-statuses [urls]
(map (^:async fn [url] (.-status (await (fetch url)))) urls))
A possible solution is to use Promise/all to await all fetches in parallel:
(defn ^:async fetch-statuses [urls]
(let [resps (await (Promise/all (mapv fetch urls)))]
(map #(.-status %) resps)))