2016-06-23 25 views
2

Ich habe Probleme, die Werte von core.async-Kanälen im Browser in der Reihenfolge zurückzugeben, in der sie erstellt wurden (im Gegensatz zur Reihenfolge, in der sie einen Wert zurückgeben). Die Kanäle selbst werden vom Mapping cljs-http.client/get über eine Liste von URLs zurückgegeben.Wie übertrage ich eine Liste von asynchronen Kanälen in der Reihenfolge, in der sie in einer Liste vorhanden sind?

Wenn ich die Ergebnisse manuell in einem let Block binde, dann kann ich die Ergebnisse in der Reihenfolge der Kanäle "von Hand" zurückgeben, aber dies ist offensichtlich ein Problem, wenn ich nicht weiß, wie viele Kanäle existieren.

(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)))) 

Aber wenn ich versuche, <! über die Kanäle abzubilden, statt ihnen zu binden einzeln dann bekomme ich nur eine Liste von Kanälen statt ihrer Werte.

(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]] 
    ) 
) 

Ich vermute, es ist ein Problem mit dem Ort des go Block, aber ich es nicht außerhalb der anonymen Funktion ohne Fehler bewegen kann, die ich <! außerhalb eines go Block bin mit.

funktioniert das nicht:

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

Und ebenso wenig wie folgt aus:

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

Ich habe auch versucht die Kanäle über async/merge Zusammenführung und dann async/reduce die Werte verbinden mit aber die Ergebnisse sind in der Reihenfolge, in der die Anforderungen erfüllt wurden, nicht die Reihenfolge der Kanäle, die zusammengeführt wurden.

Kann jemand etwas Licht auf das Abrufen von Werten aus einer Liste von Kanälen in der Reihenfolge der Kanäle in der Liste werfen?

Antwort

3

In Clojure könnten Sie (map <!! response-channels) tun, aber das ist in ClojureScript nicht möglich. Was noch wichtiger ist, ist, dass es davon abgeraten wird, map - oder Lazy-Operationen im Allgemeinen - für Nebenwirkungen zu verwenden (Checkout this blog post, um zu sehen, warum). Der Grund, Ihr Code nicht die Ergebnisse erzielen Sie erwarten sind, ist die (verschachtelte) Verwendung von fn im go Block (siehe this answer):

Mit [der Clojure go-Block] stoppt Übersetzung am Funktionsgrenzen Ich meine das: Der Go-Block nimmt seinen Körper und übersetzt ihn in eine Zustandsmaschine. Jeder Aufruf an <!>! oder alts! (und einige andere) gelten als Zustandsmaschine Übergänge, wo die Ausführung des Blocks anhalten kann. An jedem dieser Punkte wird die Maschine in einen Rückruf umgewandelt und an den Kanal angeschlossen. Wenn dieses Makro eine fn Form erreicht, stoppt es . Sie können also nur innerhalb eines Go-Blocks von <! aus anrufen, nicht innerhalb einer Funktion innerhalb eines Codeblocks.

Ich bin nicht ganz sicher, aber wenn man einen Blick auf (source map) haben werden Sie sehen, dass es fn directely sowie über andere Funktionen (wie lazy-seq) aufruft, was wahrscheinlich ist, warum (go (map <! response-channels)) nicht der Fall ist Arbeit.

Wie auch immer, wie etwa doseq:

(go (doseq [c response-channels] 
     (println (<! c)))) 

Dies wird die Bestellung innerhalb von response-channels respektieren.

+0

Vielen Dank! Das hat den Trick gemacht. Übrigens, wissen Sie, warum doseq wo arbeitet als (go (in (map user2540914

+0

Siehe bearbeiten (mein Bestes versucht) ... – beatngu13