簡體   English   中英

如何耗盡通道的值然后返回結果(ClojureScript)?

[英]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->vectorinto

雖然可以在Clojure和許多其他語言中使用,但在ClojureScript中您無法做到。

您想要一個在收聽頻道時阻止的功能。 但是,ClojureScript的core.async版本不包含阻塞運算符 為什么? 因為ClojureScript沒有阻止。

我找不到可靠的來源來支持最后一句話。 網絡上的這個主題似乎有很多混亂。 但是,我很清楚我在說什么,因為ClojureScript最終成為JavaScript,這就是JavaScript的工作方式。

實際上,JavaScript 從不會阻塞 ,無論是在瀏覽器上還是在Node.js中 為什么? 據我所知,它使用單個線程,因此如果要阻止,用戶將無法在瀏覽器中執行任何操作。

所以做你想做的事是不可能的。 這是設計,因為它可能具有災難性的UX效果。 ClojureScript頻道就像JavaScript事件; 以同樣的方式,您不希望事件偵聽器在等待事件發生時阻止用戶界面,您也不希望在等待新值時阻塞通道。

相反,嘗試使用每當傳遞新值時調用的回調函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM