1

Is there a way in Clojurescript create an async function or a macro wrap a function into a Promise to simulate it? My current use-case is to replace the following function that takes a callback by its async version - btw this is for an AWS lambda function.

// Old style
function(args, callback) {
  // Use callback(e) for errors
  // Use callback(null, value) for the result
}

// New style
async function(args) {
  return value; // success path
  throw new Error(); // error path
}

Given that this is Clojurescript, using await is not the question. And I know this can simply return a Promise to comply with the async requirement. So it resolves to some sugar code to create the Promise, catch all errors for me and calling resolve on the happy path or reject otherwise.

Browsing through clojure.core.async and docs -including the clojurescript reference, I haven't found anything.

1 Answer 1

3

Node 8 and newer ship with util.promisify that does what you want:

Takes a function following the common error-first callback style, i.e. taking a (err, value) => ... callback as the last argument, and returns a version that returns promises.

EDIT: I spent a bit writing a macro that does promisify and I'm safisfied with the result. Note that the macro needs to be saved in a CLJC file:

;; macros.cljc ;;;;;;;;;;
(ns server.macros)

(defmacro promisify [method obj params]
  `(js/Promise.
    (fn [resolve# reject#]
      (~method ~obj ~params
       (fn [err# result#]
         (if err#
           (reject# err#)
           (resolve# result#)))))))

;; main.cljs ;;;;;;;;;;
(ns server.main
  (:require-macros [server.macros :refer [promisify]])
  (:require ["aws-sdk" :as aws]))

(defn main! []
  (println "App loaded...")
  (let [creds (aws/SharedIniFileCredentials. #js {:profile "example-profile"})
        _     (set! (.-credentials aws/config) creds)
        s3    (aws/S3.)]
    (-> (promisify .listBuckets s3 #js {})
        (.then #(println "DATA:" %))
        (.catch #(println "ERROR:" %)))))

and the output is the same as before:

$ node target/main.js

App loaded...
DATA: #js {:Buckets #js [#js {:Name demo-test-bucket, :CreationDate #inst "2019-05-05T17:32:17.000-00:00"} #js {:Name subdomain.mydomain.com, :CreationDate #inst "2019-06-19T04:16:10.000-00:00"}], :Owner #js {:DisplayName username, :ID 9f7947b2d509e2338357d93e74f2f88a7528319ab3609b8d3b5be6b3a872dd2c}}

The macro is basically a Clojure version of this code.

EDIT 2: There's also this library that could be interesting if you really want to use core.async too.

Sign up to request clarification or add additional context in comments.

2 Comments

Many thanks. I must say I still hope for a solution in clojure.core.async, as it is referenced as THE solution for interop with JS in terms of Promise. If nothing comes, I'll mark this as the solution.
Many thanks for the updated answer but I realized that this is not the right answer. By converting to an async method, I want to not have to care about the callback argument, while promisify only converts an existing function requiring the callback into a promise. This function to convert is me and it still has to use the callback but the end user - AWS in my case - won't. I'll look at cljs-promises

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.