我想将子进程的 stdout 和 stderr 组合成一个单一的中间流,然后我可以使用它并进行处理。
这可以通过标准 API 实现吗?
(我意识到交错两个流有一些微妙之处,我需要它们只在行结尾上交错才能工作。即,向 stdout 或 stderr 写入一行应该是一个原子操作)。
这应该按预期工作:
// This custom stream merges stdout and stderr.
// Instead of concatenating the two streams, it keeps their interleaving.
const stdoutStderrInterleavedStream = (
stdout: ReadableStreamDefaultReader<Uint8Array>,
stderr: ReadableStreamDefaultReader<Uint8Array>
) =>
new ReadableStream({
start(controller) {
// These sentinel values are used to know if both stream have been stopped.
let stdoutDone = false
let stderrDone = false
type SentinelFunctions = {
getDone: () => boolean
setDone: () => void
}
// stdout will check if stderr is done
const sentinelStdout: SentinelFunctions = {
getDone: () => stderrDone,
setDone: () => (stdoutDone = true),
}
const sentinelStderr: SentinelFunctions = {
getDone: () => stdoutDone,
setDone: () => (stderrDone = true),
}
const push = () => {
const onData =
({ getDone, setDone }: SentinelFunctions) =>
({ done, value }: ReadableStreamDefaultReadResult<Uint8Array>) => {
if (done) {
setDone()
if (getDone()) {
controller.close()
}
return
}
controller.enqueue(value)
push()
}
stdout.read().then(onData(sentinelStdout))
stderr.read().then(onData(sentinelStderr))
}
push()
},
})
使用示例:
const stream = stdoutStderrInterleavedStream(
myDenoProcess.stdout.getReader(),
myDenoProcess.stderr.getReader()
)
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream())
const output = await Array.fromAsync(stream)
console.log(output)