Node.js 如何从 stream.Readable 同步读取行
Node.js how to synchronously read lines from stream.Readable
我正在通过 stdio 与子进程交互,并且 每次我向 childProcess.stdin 写入一些命令时,我都需要等待来自 childProcess.stdout 的一行。
像下面这样包装一个异步方法很容易写:
async function write(data){
return new Promise(resolve=>{
childProcess.stdin.write(data,()=>resolve());
})
}
然而,当涉及到读取时,它变得相当困难,因为来自 stdout 的数据必须使用侦听器进行处理。我试过以下:
const LineReader = require("readline")
const reader = LineReader.createInterface(childProcess.stdout);
async function read(){
return new Promise(resolve=>{
reader.once("line",line=>resolve(line));
})
}
但它总是returns第一行。
我知道我可以使用 setInterval 来实现这一点,而且我已经通过这种方式实现了该功能。但是很明显对性能有影响,所以现在想通过包装成异步方法来优化一下。
任何建议和解决方案将不胜感激!
好吧,我最终得到的结果与您正在尝试的非常相似。它做了代码中提到的一些假设,需要更完整的错误处理:
const cp = require('child_process');
const readline = require('readline');
const child = cp.spawn("node", ["./echo.js"]);
child.on('error', err => {
console.log(err);
}).on('exit', () => {
console.log("child exited");
});
const reader = readline.createInterface({ input: child.stdout });
// this will miss line events that occurred before this is called
// so this only really works if you know the output comes one line at a time
function nextLine() {
return new Promise(resolve => {
reader.once('line', resolve);
});
}
// this does not check for stdin that is full and wants us to wait
// for a drain event
function write(str) {
return new Promise(resolve => {
let ready = child.stdin.write(str, resolve);
if (!ready) {
console.log("stream isn't ready yet");
}
});
}
async function sendCmd(cmd) {
// get line reader event handler installed so there's no race condition
// on missing the return event
let p = nextLine();
// send the command
await write(cmd);
return p;
}
// send a sequence of commands and get their results
async function run() {
let result1 = await sendCmd("hi\n");
console.log(`Got '${result1}'`);
let result2 = await sendCmd("goodbye\n");
console.log(`Got '${result2}'`);
let result3 = await sendCmd("exit\n");
console.log(`Got '${result3}'`);
}
run().then(() => {
console.log("done");
}).catch(err => {
console.log(err);
});
并且,出于测试目的,我 运行 使用此 echo 应用程序:
process.stdin.on("data", data => {
let str = data.toString();
let ready = process.stdout.write("return: " + str, () => {
if (str.startsWith("exit")) {
process.exit();
}
});
if (!ready) {
console.log("echo wasn't ready");
}
});
我正在通过 stdio 与子进程交互,并且 每次我向 childProcess.stdin 写入一些命令时,我都需要等待来自 childProcess.stdout 的一行。
像下面这样包装一个异步方法很容易写:
async function write(data){
return new Promise(resolve=>{
childProcess.stdin.write(data,()=>resolve());
})
}
然而,当涉及到读取时,它变得相当困难,因为来自 stdout 的数据必须使用侦听器进行处理。我试过以下:
const LineReader = require("readline")
const reader = LineReader.createInterface(childProcess.stdout);
async function read(){
return new Promise(resolve=>{
reader.once("line",line=>resolve(line));
})
}
但它总是returns第一行。
我知道我可以使用 setInterval 来实现这一点,而且我已经通过这种方式实现了该功能。但是很明显对性能有影响,所以现在想通过包装成异步方法来优化一下。
任何建议和解决方案将不胜感激!
好吧,我最终得到的结果与您正在尝试的非常相似。它做了代码中提到的一些假设,需要更完整的错误处理:
const cp = require('child_process');
const readline = require('readline');
const child = cp.spawn("node", ["./echo.js"]);
child.on('error', err => {
console.log(err);
}).on('exit', () => {
console.log("child exited");
});
const reader = readline.createInterface({ input: child.stdout });
// this will miss line events that occurred before this is called
// so this only really works if you know the output comes one line at a time
function nextLine() {
return new Promise(resolve => {
reader.once('line', resolve);
});
}
// this does not check for stdin that is full and wants us to wait
// for a drain event
function write(str) {
return new Promise(resolve => {
let ready = child.stdin.write(str, resolve);
if (!ready) {
console.log("stream isn't ready yet");
}
});
}
async function sendCmd(cmd) {
// get line reader event handler installed so there's no race condition
// on missing the return event
let p = nextLine();
// send the command
await write(cmd);
return p;
}
// send a sequence of commands and get their results
async function run() {
let result1 = await sendCmd("hi\n");
console.log(`Got '${result1}'`);
let result2 = await sendCmd("goodbye\n");
console.log(`Got '${result2}'`);
let result3 = await sendCmd("exit\n");
console.log(`Got '${result3}'`);
}
run().then(() => {
console.log("done");
}).catch(err => {
console.log(err);
});
并且,出于测试目的,我 运行 使用此 echo 应用程序:
process.stdin.on("data", data => {
let str = data.toString();
let ready = process.stdout.write("return: " + str, () => {
if (str.startsWith("exit")) {
process.exit();
}
});
if (!ready) {
console.log("echo wasn't ready");
}
});