为什么这个控制台会记录两次?

gsk*_*pka 5 javascript monads functional-programming node.js console.log

我正在做的事情:

我想使用Node在特定时间按特定顺序启动两个子进程,控制台stdout在流式传输时记录它们,偶尔在两者之间切换.

我想要的输出:

`Proc 1 log # 1`
`Proc 1 log # 2`
`Proc 1 log # 3`
`Proc 1 log # 4`
`Proc 2 log # 1`
`Proc 2 log # 2`
`Proc 2 log # 3`
`Proc 2 log # 4`
`Proc 1 log # 9`
`Proc 1 log # 10`
`Proc 1 log # 11`
`Proc 1 log # 12`
`Proc 1 log # 13`
`Proc 1 log # 14`
`Proc 1 log # 15`
`All procs have finished!`
Run Code Online (Sandbox Code Playgroud)

 

当然,这很容易做到.势在必行.但它也非常丑陋和有状态,只是呃.所以我试图纯粹地做,并使用Task民间故事中的monad(旧的那个)建立计算,用手像笨蛋一样穿过有状态的对象:

 

//    main _ :: Task error {childProcs}
const main = startProc1({})
  .chain(logUntilProc1IsReady)
  .chain(startProc2)
  .chain(logUntilProc2IsReady)
  .chain(logUntilProc1IsFinished)
Run Code Online (Sandbox Code Playgroud)

 

更漂亮.它会更好,如果它工作!

 

我得到的输出:

 

`Proc 1 log # 1`                                       
`Proc 1 log # 2`
`Proc 1 log # 3`
`Proc 1 log # 4`
`Proc 2 log # 1`
`Proc 1 log # 6`   // <-- These should not be logged
`Proc 2 log # 2`
`Proc 1 log # 7`
`Proc 2 log # 3`
`Proc 1 log # 8`
`Proc 2 log # 4`
`Proc 1 log # 9`
`Proc 1 log # 10`  // <-- now it's logging twice! :confounded:
`Proc 1 log # 10`
`Proc 2 log # 6`
`Proc 1 log # 11`
`Proc 1 log # 11`
`Proc 2 log # 7`
`Proc 1 log # 12`
`Proc 1 log # 12`
`Proc 2 log # 8`
`Proc 1 log # 13`
`Proc 1 log # 13`
`Proc 2 log # 9`
`Proc 1 log # 14`
`Proc 1 log # 14`
`Proc 2 log # 10`
`All procs have finished!`
Run Code Online (Sandbox Code Playgroud)

 

我做的东西:

这是日志记录功能:

 

//    logStreamUntil :: int -> (a -> bool) -> proc -> string -> Task error State () {childProcs}
const logStreamUntil = curry((predFunc, procName, procObj) => 
  new Task ((_, res) => {
    const proc = procObj[procName]
    const logUntilPred = data =>
      predFunc(data) 
        ? (rmAllListeners(proc), res(procObj))
        : console.log(data)
    proc.stdout.on('data', logUntilPred)
}))
Run Code Online (Sandbox Code Playgroud)

 

其中的TL;博士:是,我送它一个进程的名字和采摘从实际子过程,以及用来判定多久控制台日志中的谓词函数对象stdout哪个子过程中被扔了.谓词只是查找字符串中的特定内容stdout.因此,当谓词函数返回false时,控制台会记录输出,否则它会停止记录,删除侦听器,那应该就是这样!

 

然后是rmAllListeners功能:

 

//    rmAllListeners :: childProc -> childProc           
const rmAllListeners = proc => (proc.removeAllListeners(), proc.stdout.unref())
Run Code Online (Sandbox Code Playgroud)

 

后者显然是个问题.听众虽然被命名为名称并且被上述内容所抹杀,但并不存在.我不知道为什么?!?救命!

 

进一步阅读:

对于那些有兴趣看到整件事的人​​来说,也有一个回购:你可以在这里找到它.

 

小智 2

proc您正在从 中而不是从中删除侦听器stdout。出现双打是因为您将侦听器的第二个副本附加到proc.stdout.

添加对我来说修复.stdoutrmAllListeners它:

diff --git a/why-log-twice.js b/why-log-twice.js
index 276d15c..6c15467 100644
--- a/why-log-twice.js
+++ b/why-log-twice.js
@@ -7,7 +7,7 @@ const PROC_ONE_PATH = `node child-proc "Proc 1 log # "`
 const PROC_TWO_PATH = `node child-proc "Proc 2 log # "`

 //    rmAllListeners :: childProc -> childProc           
-const rmAllListeners = proc => (proc.removeAllListeners(), proc.stdout.unref())
+const rmAllListeners = proc => (proc.stdout.removeAllListeners(), proc.stdout.unref())

 //    procIsReady :: string -> bool
 const procIsReady = str => str.includes('5')
Run Code Online (Sandbox Code Playgroud)

  • 事后看来,很多答案都是令人震惊的(你或你同事的!)。我们都被这样困住了。这是一个非常好的问题,也是一个很好的答案。很高兴您来到这里! (2认同)