我试图在函数中包装一个go例程(如下面的方法1)。方法2完全正常,但方法1没有。唯一的区别是在方法1中,我将一个通道作为参数传递给一个函数,并且put在函数内部。关于例程和函数的确切规则是什么?
(defn doit [ch i]
(print "g\n")
(async/>! ch i)
(print "f\n"))
;方法1
(let [c1 (async/chan)]
(async/go (while true
(let [[v ch] (async/alts! [c1])]
(println "Read" v "from" ch))))
(dotimes [i 10]
(async/go (doit c1 i))))
;方法2
(let [ch (async/chan)]
(async/go (while true
(let [[v ch] (async/alts! [ch])]
(println "Read" v "from" ch))))
(dotimes [i 10]
(async/go
(print "g\n")
(async/>! ch i)
(print "f\n"))))
我还注意到,如果我删除方法1中的go并将其移动到do it功能,如下所示,该函数将打印" g"但不是" f"但否则工作正常。为什么呢?
(defn doit [ch i]
(async/go
(print "g\n")
(async/>! ch i)
(print "f\n")))
答案 0 :(得分:1)
>!
和<!
并非真正被调用的函数(请参阅here和here)。 go
宏标识这两个符号,并根据这两个运算符的语义自动生成代码。所以这个宏无法知道函数在内部使用>!
或<!
运算符,因为它只是调用该函数的表单。
方法1 实际上是在每次调用doit
时抛出异常,因为>!
和<!
的实际代码只是一个始终失败的断言。在以lein repl
开头的REPLy会话中评估此方法的代码会多次显示异常Exception in thread "async-dispatch-46" java.lang.AssertionError: Assert failed: >! used not in (go ...) block
(确切地说是10)。如果您通过nREPL客户端使用REPL,则可能看不到这一点,因为异常是在服务器中异步抛出的,客户端没有考虑到这一点。
此外,您可以使用(print "something\n")
而不是使用(println "something")
,而不是与您的问题真正相关,但我认为我提到了这一点。