[英]How to exhaust a channel's values and then return the result (ClojureScript)?
假設通道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!
來自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中不存在阻塞讀取( <!!
),您可以通過以下方式重寫它:
(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建議的方法)。
使用into
返回一個通道,其中包含從連接到提供的集合的通道中獲取的項目的單個(集合)結果。 ch必須在生成結果之前關閉。
這樣的東西應該工作(它應該打印來自events-chan
給定事件 - 陳完成發布事件時關閉):
(go
(println (<! (into [] events-chan))))
源通道需要結束( 關閉 ),否則您無法將所有事件放入集合中。
編輯 :
重新閱讀你的問題,並不是很清楚你想要完成什么。 無論你想做什么, chan->vector
需要返回一個頻道,這樣無論誰調用它都可以等待結果。 事實上, chan->vector
是完全into
:
; chan->vector ch:Chan<Event> -> Chan<Vector[Event]>
(defn chan->vector [ch]
(into [] ch))
(go
(let [events (<! (chan->vector events-chan))]
(println events) ; Do whatever with the events vector
))
正如我上面提到的,如果事件陳永遠不會關閉,那么你必須更多地考慮如何使用事件。 沒有神奇的解決方案。 您想按時間間隔批量處理事件嗎? 按事件數量? 通過這些組合?
總之,如上所述, chan->vector
是into
。
雖然可以在Clojure和許多其他語言中使用,但在ClojureScript中您無法做到。
您想要一個在收聽頻道時阻止的功能。 但是,ClojureScript的core.async版本不包含阻塞運算符 。 為什么? 因為ClojureScript沒有阻止。
我找不到可靠的來源來支持最后一句話。 網絡上的這個主題似乎有很多混亂。 但是,我很清楚我在說什么,因為ClojureScript最終成為JavaScript,這就是JavaScript的工作方式。
實際上,JavaScript 從不會阻塞 ,無論是在瀏覽器上還是在Node.js中 。 為什么? 據我所知,它使用單個線程,因此如果要阻止,用戶將無法在瀏覽器中執行任何操作。
所以做你想做的事是不可能的。 這是設計,因為它可能具有災難性的UX效果。 ClojureScript頻道就像JavaScript事件; 以同樣的方式,您不希望事件偵聽器在等待事件發生時阻止用戶界面,您也不希望在等待新值時阻塞通道。
相反,嘗試使用每當傳遞新值時調用的回調函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.