请在 2024 Clojure 调查问卷! 中分享您的想法。

欢迎!请查看 关于 页面以获取更多有关如何使用此网页的信息。

+1 投票
ClojureScript

ClojureScript 有没有一种方法可以在JS中与 AsyncIterable 接口一起工作。JS 中的方法是用 for await of
对于普通的 async/await 或 promise,我找到了 - https://script.clojure.org/guides/promise-interop

我正在尝试构建一个 IPFS 文件浏览器,并且它的 JS api 返回 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 为代码的 js* 部分 - 我不知道 aget 能否查找符号)

我认为我们应该有一个辅助函数将 AsyncIterable 转换为会传递多个值的 chan。我们不打算做这些语法上的事情。以下是跟踪内容 -https://clojure.atlassian.net/browse/ASYNC-239
...