There is nothing built in that works in the same way as
for await (let thing of asyncIterable) { ... }
And, as far as I know, there isn't any library which implements a similar syntactic convenience.
But, to my understanding, the protocol for an AsyncIterable
is that there is a method keyed under Symbol.asyncIterable
. This method returns an AsyncIterator
, which is object with a next
method that when called returns a Promise<{ done: false, value: T } | { done: true }>
.
This is a similar situation to the iterator protocol, where code that looks like this
for (let o of iterable) { ... }
effectively becomes
let iterator = iterable[Symbol.iterator]();
while (true) {
let next = iterator.next();
if (next.done) {
break;
}
else {
let o = next.value;
...
}
}
instead code that looks like this
for await (let o of asyncIterable) { ... }
effectively becomes this
let asyncIterator = asyncIterable[Symbol.asyncIterable]();
while (true) {
let next = await iterator.next();
if (next.done) {
break;
}
else {
let o = next.value;
...
}
}
and then the await can be thought of as being transformed by the runtime into promise chaining.
If we want to have a similar construct in CLJS, we need to mimic these basic semantics.
This is my attempt at making a doseq
for async iterables. I haven't tested it well and I'm sure other people will have better ideas, but its a start.
(defmacro async-doseq [[binding async-iterable] & body]
`(let [async-iterator# ((js* "~{}[Symbol.asyncIterator]" ~async-iterable))]
(fn handle-next# []
(let [next# (.next async-iterator#)]
(.then
next#
(fn [res#]
(if (.- res# done)
nil
(do (let [~binding (.- res# value)]
~@body)
(handle-next#)))))))))
and you should be able to use it to build up other abstractions, like an async version of for
(defmacro async-for [[binding async-iterable] & body]
`(let [results# (transient [])]
(-> (async-doseq [~binding ~async-iterable]
(conj! results# (do ~@body)))
(.then (fn [_] (persistent! results#))))))
;; If this yields Promise<"a">, Promise<"b">, Promise<"c">
;; this expression should yield Promise<["A" "B" "C"]>
(async-for [node-name (ipfs/node-ids ...)]
(string/upper-case node-name))
(credit to MaurĂcio Szabo for the js* part of the code - I had no clue aget couldn't look up symbols)