如何按照列表中存在的顺序映射异步通道列表?

时间:2016-06-23 10:55:44

标签: http asynchronous clojure clojurescript core.async

我无法按照创建顺序从浏览器中的core.async频道返回值(而不是返回值的顺序)。通道本身是从映射cljs-http.client / get返回的URL列表返回的。

如果我在let块中手动绑定结果,那么我可以按照通道的顺序返回结果"手动",但这显然是一个问题,当我没有'知道存在多少个频道。

(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块的位置存在问题,但我无法将其移出匿名函数,而不会出现我使用{{1}的错误在<!块之外。

这不起作用:

go

这两点都没有:

(into [] (go (map <! response-channels)))

我还尝试通过(go (let [responses (into [] (map <! response-channels))])) 合并频道,然后使用async/merge来结合值,但结果按照请求完成的顺序排列,而不是合并频道的顺序。< / p>

任何人都可以根据频道列表中存在的频道从频道列表中检索值吗?

1 个答案:

答案 0 :(得分:4)

在Clojure中你可以做(map <!! response-channels),但这在ClojureScript中是不可能的。更重要的是,它不鼓励使用map - 或一般的懒惰操作 - 用于副作用(结帐this blog post以查明原因)。您的代码未能产生您期望的结果的原因是fn块中go的(嵌套)使用(请参阅this answer):

  

通过[Clojure go-block]在函数边界处停止转换,我的意思是:go块占用它的主体并将其转换为状态机。对<! >!alts!(以及其他一些)的每次调用都被视为状态机转换,其中块的执行可以暂停。在每个点上,机器变成回调并连接到通道。当此宏达到fn格式时,会停止翻译。因此,您只能从go块内部调用<!,而不是在代码块内的函数内部。

我不太确定,但是当你看到(source map)时,你会看到它直接调用fn以及通过其他函数调用lazy-seq。 1}}),这可能是(go (map <! response-channels))无效的原因。

无论如何,doseq

怎么样?
(go (doseq [c response-channels]
      (println (<! c))))

这将尊重response-channels内的订单。