This post has been heavily edited. Initially it was describing some behavior I didn't expect using core.async in both ClojureScript and Clojure. It turns out the behavior I observed in ClojureScript was a bug, that has now been fixed. The Clojure and ClojureScript versions can still behave differently than each other, depending on the lifetime of the main thread in Clojure. I have rewritten this to focus on that.
I created a buffered channel in ClojureScript, and tried to put 3 values into it:
(let [c (chan 1)] (go (.log js/console (<! c))) (go (>! c 1) (.log js/console "put one") (>! c 2) (.log js/console "put two") (>! c 3) (.log js/console "Put all my values on the channel")))
The result:
put one put two 1
Running the equivalent code in a Clojure REPL
(let [c (chan 1)] (go (println (<! c))) (go (>! c 1) (println "put one") (>! c 2) (println "put two") (>! c 3) (println "put all of my values on the channel")))
Gives the same 3 lines printed out though which line has the number 1 can change from invocation to invocation.
put one 1 put two
When I put this in a -main method and ran it with lein run, I got no results at all.
Adding Thread/sleep the -main function causes the expected results to return. Though, again which order the 1 gets printed in can vary.
(defn -main [& args] (let [c (chan 1)] (go (println (<! c))) (go (>! c 1) (println "put one") (>! c 2) (println "put two") (>! c 3) (println "put all of my values on the channel")) (Thread/sleep 1000)))
put one put two 1
Putting the main thread to sleep gives the go blocks time to execute. For confirmation of what is happening, we can check what thread each operation is happening on.
(defn -main [& args] (let [c (chan 1)] (go (println (<! c)) (println (str "out " (.getId (Thread/currentThread))))) (go (>! c 1) (println "put one") (println (str "in1 " (.getId (Thread/currentThread)))) (>! c 2) (println "put two") (println (str "in2 " (.getId (Thread/currentThread)))) (>! c 3) (println "put all of my values on the channel")) (Thread/sleep 1000) (println (str "main " (.getId (Thread/currentThread))))))
When I ran it I had the first go block on thread 10, the second on 11 and the main thread of execution on thread 1.
put one in1 11 put two in2 11 1 out 10 main 1
If we remove the Thread/sleep expression, but leave the calls to println, our output is:
main 1
This is just demo code. I don't know when in production systems your main thread would be likely to exit before asynchronous tasks are complete.
I do think it is interesting that the blocks of code I have run have executed in the same order every time in ClojureScript, but that the order can vary when running on the JVM. I do not know if that would be true of more complicated examples or on different browsers. I have done all of my tests with Google Chrome.
This comment has been removed by the author.
ReplyDeleteThis is a bug in CLJS implementation. I just pushed a fix to master , and it should be included in the next release.
ReplyDeletehttp://tinyurl.com/mplpkxb
Thanks very much for the comment. I originally was researching this to file a bug report but I (mistakenly) convinced myself it was working correctly. I will edit the blog post to reflect the changes.
Delete