(go (>! c 42))
以下是很容易做到的,用來傳送訊息而不必等待回覆
(go (>! c 42))
雖然 go block 很便宜,但並非完全免費。因此建議使用
(async/put! c 42)
go
最終只會呼叫 put!
,所以實際上沒有缺點。
此外,如果程式碼是在回呼中呼叫,而且您想要尊重反壓力,則可以很容易地使用遞迴函數搭配 put!
來尊重反壓力。
(defn http-call
"Makes an async call to a web browser"
[url callback] ...)
(def urls [url1 url2 url3])
(defn load-urls
"Spools the results of loading several urls onto a channel.
does this without creating temporary channels or go blocks"
[urls out-c]
(http-call
(first urls)
(fn [response]
(put! out-c response (fn [_] (load-urls (next urls) out-c))))))
(load-urls urls response-chan)
在此範例中,我們有一些不錯的乾淨互通操作程式碼,讓我們可以在應用程式中開始使用通道,而不用建立大量通道或 gos,只為了在建立後不久就將它們處置掉。
請記住,尊重反壓力很重要。core.async 的一般原則是不定界佇列很糟糕,而且未決的 put 數量有限(目前為 1024)。另一個選項是使用始終立即接受 put 的通道,例如 dropping-buffer
或 sliding-buffer
。
go 巨集會在函式建立的邊界停止轉譯。這表示以下程式碼會無法編譯,或可能只會擲出一個執行時期錯誤,指出 <!
在 go 區塊外使用
(go (let [my-fn (fn [] (<! c))] (my-fn)))
這是需要記住的一件事,因為許多 Clojure 結構會在巨集中建立函式。以下是不會如預期般運作的程式碼範例
(go (map <! some-chan))
(go (for [x xs]
(<! x)))
然而,其他 Clojure 結構,例如 doseq
,並不會在內部配置封閉函式
; This works just fine
(go (doseq [c cs]
(println (<! c)))
不幸的是,目前沒有好的方法可以知道給定的巨集是否會在 go 區塊中如預期般運作,除非查看原始碼或測試巨集產生的程式碼。
對於「為什麼 go 區塊轉譯會在函式建立時停止?」的最佳解釋基本上歸結為型別問題。檢視以下片段
(map str [1 2 3])
我們可以輕易看出這會產生一個字串的 seq
,因為 str
的輸出型別是一個字串。那麼 async/<!
的回傳型別是什麼?在 go 區塊的語境中,它是一個從通道取得的物件。但 go 區塊必須將其轉譯為對 async/put!
的暫停呼叫。async/<!
的回傳型別實際上應該被視為類似於 Async<Object>
或 Promise<Object>
的東西。因此 (map async/<! chans)
的結果類似於「一個待處理通道操作的 seq」,這完全沒有意義。
簡而言之,go 巨集無法在沒有大量工作的情況下執行這些操作。其他語言,例如 Erjang,允許透過轉譯整個 JVM 中的所有程式碼來建立此類結構。這是我們希望在 core.async 中避免的事情,因為它會使事情複雜化,並導致一個函式庫的邏輯感染整個 JVM 的程式碼。因此,我們只剩下實用的折衷方案,當看到 (fn [] …)
時,轉譯就會停止。
原始作者:Timothy Baldridge