Reputation: 743
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
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