How to deal with js/async and js/await in ClojureScript?

I am implementing in Clojurescript (re-frame) code that looks like this:

const login = async () => {
  await auth0Client.loginWithRedirect({
    authorizationParams: {
      redirect_uri: window.location.origin
    }
  });
};

But I am not sure how best to translate the async and the await commands. Any suggestions?

1 Like

I swear this documentation wasn’t here when I looked previously… ClojureScript - Promise interop

If you are using shadow-cljs there is also

But you picked an example that doesn’t need either the async keyword nor the await.

This is functionally equivalent to the async/await JS variant you posted.

(defn login []
  (.loginWithRedirect auth0client
    (clj->js
      {:authorizationParams
       {:redirect_uri js/window.location.origin}})))

Or if you really must use async and maybe want to do something with the result, you can do so with js-await from shadow-cljs.

(defn login []
  (js-await [result
             (.loginWithRedirect auth0client
               (clj->js
                 {:authorizationParams
                  {:redirect_uri js/window.location.origin}}))]
    result
    (catch failure
      (prn [:login-failed failure])
      ::fail!)))

;; or regular old .then interop
(defn login []
  (-> (.loginWithRedirect auth0client
        (clj->js
          {:authorizationParams
           {:redirect_uri js/window.location.origin}}))
      (.then (fn [result]
               result))
      (.catch (fn [failure]
                (prn [:login-failed failure])
                failure))))

An async function in JS is nothing but a regular function that returns a Promise. If all you do is call a function that returns a promise then you can just return that and you are done. You can of course to .then interop if you want. I strongly recommend NOT using core.async for Promise interop, if that is your only use of core.async. It is fine if you are already using core.async elsewhere.

Why do you recommend not using core.async? Larger bundle size?

I specifically highlighted “if that is your only use of core.async”. So, if you only use of core.async is promise handling then it is absolutely wasteful. Build size, performance and general ergonomics just suffer without any benefit. You write more verbose code and lose access to the underlying Promise, which in some contexts (e.g. Debugging, Inspection) can be very useful.

1 Like

OP, not sure if you’ve ever used promesa library but I find it makes working with promises very convenient, especially the p/let form. Basically the RHS of each binding pair is resolved into a value before passing any further down. Just be aware that the @ syntax in the docs only works in CLJ.

https://funcool.github.io/promesa/latest/promises.html

Example:

;; Return value of this p/let block is a promise for the last expression
(p/let [result (this-function-returns-a-promise)]
  ;; do-something-with-result expects a value as it's argument, not a promise
  (do-something-with-result result)) ;; Here result is a value, no longer a promise
1 Like

Ok, good to know. I have little experience with core.async, is the performance that bad? I don’t find the ergonomics in the linked example bad, though. Granted, it may be an unnecessary abstraction over Promises when used solely for that, but might be worth it to get to know core.async? I don’t know…

Performance is fine, but on the client your resource budget is already limited as it is. If you can save time and bandwidth, then you should. core.async for just promise handling is just overkill and not what it was designed for (i.e. CSP). You of course can but to me that always feels like getting in my car and drive 10 meters instead of just walking. :stuck_out_tongue:

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.