2024 Clojure状态调查!中分享您的想法。

欢迎!有关如何运作的更多信息,请参阅关于页面。

+1
ClojureScript

ClojureScript是否有任何方式在JavaScript中处理AsyncIterable接口。JavaScript的方式是使用for await of
对于普通的async/await或promise,我找到了 - https://script.clojure.org/guides/promise-interop

我正在尝试构建一个IPFS文件浏览器,其JavaScriptAPI返回AsyncIterable。

1 条回答

0

编辑
 
最佳回答

没有任何内置函数与它以相同的方式工作。

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 能不能查找符号)

我认为我们应该有一个辅助函数将 AsyncIterable 转换为将多次传递值的通道。我们不会考虑语法上的事。以下是追踪记录 -https://clojure.atlassian.net/browse/ASYNC-239
...