没有任何内置函数与它以相同的方式工作。
for await (let thing of asyncIterable) { ... }
据我所知,也没有任何库实现类似的语法便捷性。
但是,根据我的理解,AsyncIterable
协议中存在一个键为 Symbol.asyncIterable
的方法。这个方法返回一个 AsyncIterator
对象,该对象有一个 next
方法,当调用时返回一个 Promise<{ done: false, value: T } | { done: true }>
。
这类似于迭代器协议中类似的情况,其中看似这样的代码
for (let o of iterable) { ... }
实际上变为
let iterator = iterable[Symbol.iterator]();
while (true) {
let next = iterator.next();
if (next.done) {
break;
}
else {
let o = next.value;
...
}
}
而不是这样的代码
for await (let o of asyncIterable) { ... }
实际上变为这样的代码
let asyncIterator = asyncIterable[Symbol.asyncIterable]();
while (true) {
let next = await iterator.next();
if (next.done) {
break;
}
else {
let o = next.value;
...
}
}
然后等待(await)可以看作是运行时转换成了链式调用 promise。
如果我们想在 CLJS 中实现类似的构造函数,我们需要模拟这些基本语义。
这是我尝试为异步可迭代对象创建 doseq
的例子。我没有对其进行很好的测试,相信其他人会有更好的想法,但这是一个起点。
(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#)))))))))
您可以使用它来构建其他抽象,如异步版本的 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))
(感谢 Maurício Szabo code 的 js* 部分 - 我不知道 aget 能不能查找符号)