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

欢迎!请参阅关于页面,了解有关此工作方式的一些更多信息。

+1
ClojureScript

ClojureScript 是否有方法在 JS 中与 AsyncIterable 接口一起工作。JS 方法是使用 for await of
针对常规异步/等待或 Promise,我发现 - https://script.clojure.org/guides/promise-interop

我正在尝试构建一个 IPFS 文件浏览器,它的 JS API 返回 AsyncIterable。

1 答案

0

编辑
 
最佳答案

没有内置的可以直接工作的方式

for await (let thing of asyncIterable) { ... }

据我所知,也没有任何库实现了类似的语法便捷性

但是,据我理解,AsyncIterable 的协议是在 Symbol.asyncIterable 键下存在一个方法。这个方法返回一个具有 next 方法的 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 对代码中 js* 部分的贡献 - 我不知道 aget 不能查找到符号)

我认为我们应该有一个辅助函数将 AsyncIterable 转换成一个可以传递多个值的通道。关于语法层面的东西就不做了。这里记录下来 - https://clojure.atlassian.net/browse/ASYNC-239
...