如何按照异步通道在列表中存在的顺序映射异步通道列表?
How do I map over a list of async channels in the order they exist in a list?
我在 return 浏览器中 core.async 通道中的值时遇到了问题,这些值是按照它们的创建顺序(与它们 return 值的顺序相反的).频道本身是 return 通过将 cljs-http.client/get 映射到 URL 列表而编辑的。
如果我在 let
块中手动绑定结果,那么我可以 return 按通道顺序 "by hand" 结果,但是当我不这样做时这显然是个问题不知道有多少频道。
(let [response-channels (map #(http/get "http://date.jsontest.com" {:with-credentials? false}) (range 3))]
; Response is now three channels generated by http/get:
;(#object[cljs.core.async.impl.channels.ManyToManyChannel]
; #object[cljs.core.async.impl.channels.ManyToManyChannel]
; #object[cljs.core.async.impl.channels.ManyToManyChannel])
; If I want the results back in the guaranteed order that I made them, I can do this:
(go (let [response1 (<! (nth response-channels 0))
response2 (<! (nth response-channels 1))
response3 (<! (nth response-channels 2))]
(println "This works as expected:" response1 response2 response3))))
但是如果我尝试将 <!
映射到频道而不是单独绑定到它们,那么我只会得到一个频道列表而不是它们的值。
(let [response-channels (map #(http/get "http://date.jsontest.com" {:with-credentials? false}) (range 3))]
(let [responses (into [] (map (fn [c] (go (<! c))) response-channels))]
(println "This just returns the channels:" responses)
; This is still just a vec of many-to-many channels
; [#object[cljs.core.async.impl.channels.ManyToManyChannel]
; #object[cljs.core.async.impl.channels.ManyToManyChannel]
; #object[cljs.core.async.impl.channels.ManyToManyChannel]]
)
)
我怀疑 go
块的位置有问题,但是我无法将它移出匿名函数而不会出现错误,我在 a 外部使用 <!
go
块。
这不起作用:
(into [] (go (map <! response-channels)))
这也不行:
(go (let [responses (into [] (map <! response-channels))]))
我还尝试通过 async/merge
合并通道,然后使用 async/reduce
连接值,但结果是按请求完成的顺序排列的,而不是合并通道的顺序.
任何人都可以阐明如何按照频道在列表中存在的顺序从频道列表中检索值吗?
在 Clojure 中你可以做到 (map <!! response-channels)
,但在 ClojureScript 中这是不可能的。更重要的是,不鼓励使用 map
——或者一般的惰性操作——因为副作用(checkout this blog post to see why). The reason your code doesn't yield the results you're expecting is the (nested) use of fn
within the go
block (see this answer):
By [the Clojure go-block] stops translation at function boundaries, I mean this: the go block takes its body and translates it into a state-machine. Each call to <!
>!
or alts!
(and a few others) are considered state machine transitions where the execution of the block can pause. At each of those points the machine is turned into a callback and attached to the channel. When this macro reaches a fn
form it stops translating. So you can only make calls to <!
from inside a go block, not inside a function inside a code block.
我不太确定,但是当您查看 (source map)
时,您会发现它直接调用 fn
以及通过其他函数(例如 lazy-seq
), 这可能就是 (go (map <! response-channels))
不起作用的原因。
无论如何,doseq
:
(go (doseq [c response-channels]
(println (<! c))))
这将遵守 response-channels
内的顺序。
我在 return 浏览器中 core.async 通道中的值时遇到了问题,这些值是按照它们的创建顺序(与它们 return 值的顺序相反的).频道本身是 return 通过将 cljs-http.client/get 映射到 URL 列表而编辑的。
如果我在 let
块中手动绑定结果,那么我可以 return 按通道顺序 "by hand" 结果,但是当我不这样做时这显然是个问题不知道有多少频道。
(let [response-channels (map #(http/get "http://date.jsontest.com" {:with-credentials? false}) (range 3))]
; Response is now three channels generated by http/get:
;(#object[cljs.core.async.impl.channels.ManyToManyChannel]
; #object[cljs.core.async.impl.channels.ManyToManyChannel]
; #object[cljs.core.async.impl.channels.ManyToManyChannel])
; If I want the results back in the guaranteed order that I made them, I can do this:
(go (let [response1 (<! (nth response-channels 0))
response2 (<! (nth response-channels 1))
response3 (<! (nth response-channels 2))]
(println "This works as expected:" response1 response2 response3))))
但是如果我尝试将 <!
映射到频道而不是单独绑定到它们,那么我只会得到一个频道列表而不是它们的值。
(let [response-channels (map #(http/get "http://date.jsontest.com" {:with-credentials? false}) (range 3))]
(let [responses (into [] (map (fn [c] (go (<! c))) response-channels))]
(println "This just returns the channels:" responses)
; This is still just a vec of many-to-many channels
; [#object[cljs.core.async.impl.channels.ManyToManyChannel]
; #object[cljs.core.async.impl.channels.ManyToManyChannel]
; #object[cljs.core.async.impl.channels.ManyToManyChannel]]
)
)
我怀疑 go
块的位置有问题,但是我无法将它移出匿名函数而不会出现错误,我在 a 外部使用 <!
go
块。
这不起作用:
(into [] (go (map <! response-channels)))
这也不行:
(go (let [responses (into [] (map <! response-channels))]))
我还尝试通过 async/merge
合并通道,然后使用 async/reduce
连接值,但结果是按请求完成的顺序排列的,而不是合并通道的顺序.
任何人都可以阐明如何按照频道在列表中存在的顺序从频道列表中检索值吗?
在 Clojure 中你可以做到 (map <!! response-channels)
,但在 ClojureScript 中这是不可能的。更重要的是,不鼓励使用 map
——或者一般的惰性操作——因为副作用(checkout this blog post to see why). The reason your code doesn't yield the results you're expecting is the (nested) use of fn
within the go
block (see this answer):
By [the Clojure go-block] stops translation at function boundaries, I mean this: the go block takes its body and translates it into a state-machine. Each call to
<!
>!
oralts!
(and a few others) are considered state machine transitions where the execution of the block can pause. At each of those points the machine is turned into a callback and attached to the channel. When this macro reaches afn
form it stops translating. So you can only make calls to<!
from inside a go block, not inside a function inside a code block.
我不太确定,但是当您查看 (source map)
时,您会发现它直接调用 fn
以及通过其他函数(例如 lazy-seq
), 这可能就是 (go (map <! response-channels))
不起作用的原因。
无论如何,doseq
:
(go (doseq [c response-channels]
(println (<! c))))
这将遵守 response-channels
内的顺序。