Clojure Core.async:从承诺通道集合中获取所有值
考虑这样一个数据集:Clojure Core.async:从承诺通道集合中获取所有值,clojure,core.async,Clojure,Core.async,考虑这样一个数据集: (def data [{:url "http://www.url1.com" :type :a} {:url "http://www.url2.com" :type :a} {:url "http://www.url3.com" :type :a} {:url "http://www.url4.com" :type :b}]) ;; (go (put! result-c (concat (<! (nt
(def data [{:url "http://www.url1.com" :type :a}
{:url "http://www.url2.com" :type :a}
{:url "http://www.url3.com" :type :a}
{:url "http://www.url4.com" :type :b}])
;; (go (put! result-c (concat (<! (nth chans 0))
;; (<! (nth chans 1))
;; (<! (nth chans 2))
;; (<! (nth chans 3)))))
;; instead of above, now you can do this:
(->> chans
async/merge
(async/reduce into []))
应该同时请求这些URL的内容。根据项的:type值,这些内容应该由相应的函数解析。一旦所有响应到达,解析函数将返回集合,这些集合应该连接起来
因此,让我们假设有函数parse-a
和parse-b
,当传递一个包含HTML
内容的字符串时,这两个函数都返回一个字符串集合
看起来core.async
可能是一个很好的工具。每个项目可以有单独的通道,也可以只有一个通道。我不确定这里哪条路更合适。对于多个通道,可以使用传感器进行后处理/解析。还有一个特殊的promise chan
,在这里可能是合适的
这是一个代码草图,我正在使用一个基于回调的HTTP工具包
函数。不幸的是,我在go块中找不到通用的解决方案
(defn f [data]
(let [chans (map (fn [{:keys [url type]}]
(let [c (promise-chan (map ({:a parse-a :b parse-b} type)))]
(http/get url {} #(put! c %))
c))
data)
result-c (promise-chan)]
(go (put! result-c (concat (<! (nth chans 0))
(<! (nth chans 1))
(<! (nth chans 2))
(<! (nth chans 3)))))
result-c))
(定义f[数据]
(let[chans(map(fn[{:keys[url-type]}]
(让[c(promise chan(map({:a parse-a:b parse-b}类型)))]
(http/geturl{}#(put!c%))
c) )
(数据)
结果-c(陈国荣)]
(去(放)!结果-c(混凝土)(
结果可以这样解读:
(go (prn (<! (f data))))
(go(prn(我认为promise chan
在这里弊大于利。问题是大部分core.async
API(a/merge
,a/reduce
等))依赖于这样一个事实,即通道将在某个点关闭,promise chan
反过来永远不会关闭
因此,如果坚持使用core.async
对您来说至关重要,那么更好的解决方案将不是使用promise chan
,而是使用普通通道,在第一次put!
后关闭该通道:
...
(let [c (chan 1 (map ({:a parse-a :b parse-b} type)))]
(http/get url {} #(do (put! c %) (close! c)))
c)
...
此时,您使用的是封闭通道,事情变得更简单。要收集所有值,您可以执行以下操作:
(def data [{:url "http://www.url1.com" :type :a}
{:url "http://www.url2.com" :type :a}
{:url "http://www.url3.com" :type :a}
{:url "http://www.url4.com" :type :b}])
;; (go (put! result-c (concat (<! (nth chans 0))
;; (<! (nth chans 1))
;; (<! (nth chans 2))
;; (<! (nth chans 3)))))
;; instead of above, now you can do this:
(->> chans
async/merge
(async/reduce into []))
(go)(put)(put)result-c(concat)(chans)
异步/合并
(异步/还原为[])
UPD(以下是我个人的看法):
似乎,使用core.async
通道作为承诺(无论是以promise chan
的形式,还是在单个put!
之后关闭的通道)并不是最好的方法。随着事情的发展,原来core.async
API总体上是(您可能已经注意到)虽然不是那么令人愉快。还有一些可能会迫使您编写更少的惯用代码。此外,没有内置的错误处理(如果错误发生在go
-block中,go
-block将静默返回nil
)为了解决这个问题,你需要拿出你自己的东西(重新发明轮子)因此,如果您需要承诺,我建议您为此使用特定的库,例如或。在async.core
中使用以同步启动异步操作,如http/get
,同时以与输入相同的顺序交付结果:
(let [result (chan)]
(pipeline-async
20 result
(fn [{:keys [url type]} ch]
(let [parse ({:a parse-a :b parse-b} type)
callback #(put! ch (parse %)(partial close! ch))]
(http/get url {} callback)))
(to-chan data))
result)
我也想要这个功能,因为我真的很喜欢core.async
,但我也想在某些地方使用它,比如传统的JavaScript
承诺。我想出了一个使用宏的解决方案。在下面的代码中,如果有人还在看这个问题,可以通过@OlegTheCat补充答案:
对于错误,可以使用单独的通道
(:require [cljs.core.async :as async]
[cljs-http.client :as http])
(:require-macros [cljs.core.async.macros :refer [go]])
(go (as-> [(http/post <url1> <params1>)
(http/post <url2> <params2>)
...]
chans
(async/merge chans (count chans))
(async/reduce conj [] chans)
(async/<! chans)
(<callback> chans)))
(:require[cljs.core.async:as async]
[cljs-http.client:作为http])
(:需要宏[cljs.core.async.macros:refere[go]])
(go(as->[(http/post)
(http/post)
...]
chans
(异步/合并通道(计数通道))
(异步/减少连接[]通道)
(异步/通道)
您的具体问题是什么?实际上,在此期间,我或多或少地想到了您提出的解决方案。在我决定将错误处理添加到解决方案中之前,该解决方案工作得很顺利。我可能会使用简单的承诺。