如何耗尽通道的值,然后返回结果(ClojureScript)?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (6)

假设通道chan在队列中具有值“1”和“2”。

目标:创建一个获取chan并返回向量的函数[1 2]。请注意,如果此函数在返回其值之前必须阻塞一段时间,我完全没问题

尝试:

(defn chan->vector
  [chan]  
  (let [a (atom true) v []]
    (while (not-nil? @a)
      (go
        (reset! a (<! chan))
        (into v @a)
        (reset! a (<! chan))
      )
    ) v
  )
)

结果:我的REPL冻结并最终吐出一个巨大的错误。我已经意识到这是因为(go ...)块是异步的,所以立即返回。因此,我的(while ...)循环中的原子永远不会被设置为nil,并且循环永远不会终止。

那么我该如何实现预期的结果呢?如果它是相关的,我正在使用ClojureScript和目标nodejs。

提问于
用户回答回答于

你应该使用alts!from core.async来完成这个任务(https://clojure.github.io/core.async/#clojure.core.async/alts!):

(def x (chan 10))

(go (>! x 1)
    (>! x 2)
    (>! x 3))

(defn read-all [from-chan]
  (<!! (go-loop [res []]
           (let [[v _] (alts! [from-chan] :default :complete)]
             (if (= v :complete)
               res
               (recur (conj res v)))))))

(read-all x) 
;; output: [1 2 3]

(read-all x)
;; output: []

(go (>! x 10)
    (>! x 20)
    (>! x 30)
    (>! x 40))

(read-all x)
;; output: [10 20 30 40]

里面的go-loop这个(a/alts! [from-chan] :default :complete)尝试读取信道的任何值,并且如果没有可用的一次它发出的默认值的值,所以你会看到,你应该打破循环和返回累积值。

更新:由于<!!cljs中不存在阻塞read(),您可以通过以下方式重写它:

(defn read-all [from-chan]
  (go-loop [res []]
    (let [[v _] (alts! [from-chan] :default :complete)]
      (if (= v :complete)
        res
        (recur (conj res v)))))))

所以它将返回通道,然后从那里读取一个值:

(go (let [res (<! (read-all x))]
      (println res)
      ;; do something else
      ))
用户回答回答于

你可以使用clojure.core.async/reduce

;; demo setup
(def ch (async/chan 2))
(async/>!! ch :foo)
(async/>!! ch :bar)

;; background thread to print reduction result
(async/thread
  (prn (async/<!! (async/reduce conj [] ch))))

;; closing the channel…
(async/close! ch)

;; …terminates the reduction and the result gets printed out:
;; [:foo :bar]

clojure.core.async/reduce返回一个通道,如果原始通道关闭,将生成一个值。在内部,它使用一个go块,并在从原始通道中获取元素之间释放控制。

如果您希望在经过一定时间后生成一个值,无论原始通道是否关闭,您可以将原始通道包装在传递通道中,该通道将在超时通过后自行关闭,或者您可以使用自定义方法减少步骤(也许是@leetwinski建议的方法)。

扫码关注云+社区

领取腾讯云代金券