Kineolyan
Kineolyan

Reputation: 743

How to create an async function in Clojurescript?

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.

Upvotes: 1

Views: 1348

Answers (1)

Denis Fuenzalida
Denis Fuenzalida

Reputation: 3346

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.

Upvotes: 3

Related Questions