Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
理解Clojure与ClojureScript中的core.async合并_Clojure_Clojurescript - Fatal编程技术网

理解Clojure与ClojureScript中的core.async合并

理解Clojure与ClojureScript中的core.async合并,clojure,clojurescript,Clojure,Clojurescript,我正在Clojure和ClojureScript上试用core.async,试图了解merge的工作原理。特别是,merge是否使输入通道上的任何值可立即在合并通道上获取 我有以下代码: (ns async-merge-example.core (:require #?(:clj [clojure.core.async :as async] :cljs [cljs.core.async :as async]) [async-merge-example.exec :as exec]

我正在Clojure和ClojureScript上试用
core.async
,试图了解
merge
的工作原理。特别是,
merge
是否使输入通道上的任何值可立即在合并通道上获取

我有以下代码:

(ns async-merge-example.core
  (:require
   #?(:clj [clojure.core.async :as async] :cljs [cljs.core.async :as async])
   [async-merge-example.exec :as exec]))

(defn async-fn-timeout
  [v]
  (async/go
    (async/<! (async/timeout (rand-int 5000)))
    v))

(defn async-fn-exec
  [v]
  (exec/exec "sh" "-c" (str "sleep " (rand-int 5) "; echo " v ";")))

(defn merge-and-print-results
  [seq async-fn]
  (let [chans (async/merge (map async-fn seq))]
    (async/go
      (while (when-let [v (async/<! chans)]
               (prn v)
               v)))))
对于ClojureClojureScript,我得到了我所期望的结果,如中所示,结果几乎立即开始打印,并出现了预期的延迟

但是,当我尝试使用相同的
seq
进行
async fn exec
时:

(merge-and-print-results (range 20) async-fn-timeout)
(merge-and-print-results (range 20) async-fn-exec)
对于ClojureScript,我得到了我所期望的结果,因为结果很快就开始打印,并且出现了预期的延迟。然而,对于Clojure,即使
sh
进程是并发执行的(取决于
core.async
线程池的大小),结果最初似乎是延迟的,然后大部分是一次性打印的!我可以通过增加seq的大小使这种差异更加明显,例如
(范围40)

由于
async fn timeout
的结果与Clojure和ClojureScript上的预期一样,因此指出了
exec
的Clojure和ClojureScript实现之间的差异

但我不知道为什么这种差异会导致这个问题

注:

  • 这些观察是在Windows 10上的WSL中进行的
  • 异步合并示例.exec的源代码如下
  • exec
    中,由于Clojure/Java和ClojureScript/NodeJS之间的差异,Clojure和ClojureScript的实现不同
(ns async-merge-example.exec
(:需要
#(:clj[clojure.core.async:as async]:cljs[cljs.core.async:as async]))
; 基于xml的cljs实现https://gist.github.com/frankhenderson/d60471e64faec9e2158c
; 基于xml的clj实现https://stackoverflow.com/questions/45292625/how-to-perform-non-blocking-reading-stdout-from-a-subprocess-in-clojure
#(:cljs(def spawn(.-spawn(js/require“child_process”)))
#?(:cljs)
(陈德文行政总监)
为带有args的cmd生成子进程。路由stdout、stderr和
频道的退出代码。立即返回频道。“
[cmd args]
(让[c(异步/chan),p(spawn cmd(如果args(clj->js args)(clj->js[])))]
(.on(.-stdout p)“data”#(异步/输出!c[:输出(str%)]))
(.on.(-stderr p)“data”#(async/put!c[:err(str%)]))
(.on p“close”#(async/put!c[:exit(str%)]))
c) ))
#(:clj)
(陈德文行政总监)
为带有args的cmd生成子进程。路由stdout、stderr和
频道的退出代码。立即返回频道。“
[cmd args]
(让[c(async/chan)]
(异步/去
(让[builder(ProcessBuilder)(转换为数组字符串(cons cmd(map str args))))
进程(.start builder)]
(使用open[reader(clojure.java.io/reader(.getInputStream进程))
错误读取器(clojure.java.io/reader(.getErrorStream进程))]
(循环[]
(let[line(.readLine^java.io.BufferedReader读取器)
错误(.readLine^java.io.BufferedReader err reader)]
(如果(或线路错误)
(do(当行(async/>!c[:out line]))
(当出现错误时(async/>!c[:err err]))
(重现)
(做
(.等待处理)
(async/>!c[:退出(.exitValue进程)]);(退出)
c) ))
(defn exec)
“使用args执行cmd。立即返回一个通道,该通道
最终将收到的结果地图
{:out[stdout行]:err[stderr行]:退出[exit code]}”
[cmd&args]
(让[c(行政长官陈文德)

(异步/go(循环[输出(async/您的Clojure实现在单个线程中使用阻塞IO。您首先从stdout读取,然后在循环中从stderr读取。两者都执行阻塞
readLine
,因此它们只会在实际读取完一行后返回。因此,除非您的进程向stdout和stderr创建相同数量的输出,否则一个流将结束把另一个锁上

一旦该过程完成,
readLine
将不再阻塞,一旦缓冲区为空,就返回
nil
。因此循环只是完成读取缓冲输出,然后最终完成解释“一次全部”消息

您可能需要启动第二个线程来处理从stderr读取的数据


node
不执行阻塞IO,因此默认情况下所有操作都是异步的,一个流不会阻塞另一个流。

我认为这不正确-虽然我确实在使用阻塞IO,但core.async使用8个线程池执行
go
块中的代码。我可以在执行Clojure实现时验证这一点,正如我看到的8 concurrent
dash
进程在我运行代码时立即启动。这证实了这项工作是并行完成的。也就是说,我不能解释的是,打印任何结果的明显延迟,即使
sh
进程立即启动……您可以并行运行,但每个单独的异步/去仍然可以阻塞自身。尝试在
之前添加一个prn左右(如果(或行错误)
line,在两个stdout/stderr都收到一个完整的行或者进程退出之前,您应该会看到没有任何进展。好的,我明白您的意思了:stdout/stderr,但是在任何情况下,我删除了所有与stderr相关的代码,并且在打印时仍然会得到相同的延迟。此外,我添加了
(println“processfinished for”cmd,with args“args”)
(.waitFor process)
之后立即执行,
,我在运行时立即看到输出。这表明原因与用于通信
:退出的core.async通道有关?
(ns async-merge-example.exec
  (:require
   #?(:clj [clojure.core.async :as async] :cljs [cljs.core.async :as async])))

; cljs implementation based on https://gist.github.com/frankhenderson/d60471e64faec9e2158c

; clj implementation based on https://stackoverflow.com/questions/45292625/how-to-perform-non-blocking-reading-stdout-from-a-subprocess-in-clojure

#?(:cljs (def spawn (.-spawn (js/require "child_process"))))

#?(:cljs
   (defn exec-chan
     "spawns a child process for cmd with args. routes stdout, stderr, and
      the exit code to a channel. returns the channel immediately."
     [cmd args]
     (let [c (async/chan), p (spawn cmd (if args (clj->js args) (clj->js [])))]
       (.on (.-stdout p) "data"  #(async/put! c [:out  (str %)]))
       (.on (.-stderr p) "data"  #(async/put! c [:err  (str %)]))
       (.on p            "close" #(async/put! c [:exit (str %)]))
       c)))

#?(:clj
   (defn exec-chan
     "spawns a child process for cmd with args. routes stdout, stderr, and
      the exit code to a channel. returns the channel immediately."
     [cmd args]
     (let [c (async/chan)]
       (async/go
         (let [builder (ProcessBuilder. (into-array String (cons cmd (map str args))))
               process (.start builder)]
           (with-open [reader (clojure.java.io/reader (.getInputStream process))
                       err-reader (clojure.java.io/reader (.getErrorStream process))]
             (loop []
               (let [line (.readLine ^java.io.BufferedReader reader)
                     err (.readLine ^java.io.BufferedReader err-reader)]
                 (if (or line err)
                   (do (when line (async/>! c [:out line]))
                       (when err (async/>! c [:err err]))
                       (recur))
                   (do
                     (.waitFor process)
                     (async/>! c [:exit (.exitValue process)]))))))))
       c)))

(defn exec
  "executes cmd with args. returns a channel immediately which
   will eventually receive a result map of 
   {:out [stdout-lines] :err [stderr-lines] :exit [exit-code]}"
  [cmd & args]
  (let [c (exec-chan cmd args)]
    (async/go (loop [output (async/<! c) result {}]
                (if (= :exit (first output))
                  (assoc result :exit (second output))
                  (recur (async/<! c) (update result (first output) #(conj (or % []) (second output)))))))))