我正在编写一个node.js应用程序,它将stdout传送到一个文件。我正在使用console.log编写所有内容。一段时间后,我的应用程序达到1GB限制并停止。有趣的是,如果我使用console.error而不是console.log,内存使用率会保持较低并且程序运行正常。所以看起来node.js无法刷新stdout流,所有内容都保存在内存中。我想让stderr免费提供错误。
我的问题是:
有没有办法将阻塞写入stdout?或者至少,我可以写一个回调到stdout,所以我可以确保我写的不是太多吗?
THX!
答案 0 :(得分:12)
如果你真的想要同步写入stdout,你可以这样做:
var fs = require('fs');
fs.writeSync(1, "Foo\n");
fs.fsyncSync(1);
答案 1 :(得分:6)
使用process.stdout.write
写入,返回值是数据是否被缓冲。如果是,请在process.stdout
发出drain
事件时继续撰写。
如果您希望代码看起来同步,请按照此处所述使用streamlinejs:Node.js stdout flush
答案 2 :(得分:1)
别。
当输出已满时,您想要做的是pause()
输入,就像pump()
方法那样,然后resume()
当您有空间写入时。如果没有,你的过程会膨胀到庞大的大小。
您可能希望使用更直接的outputStream
内容,或write()
来电,而不是console.log()
。
答案 3 :(得分:0)
使用Async / await,同步打印功能也可以与管道(也称为FIFO)一起使用。 确保始终将“打印”与“等待打印”一起调用
let printResolver;
process.stdout.on('drain', function () {
if (printResolver) printResolver();
});
async function print(str) {
var done = process.stdout.write(str);
if (!done) {
await new Promise(function (resolve) {
printResolver = resolve;
});
}
}
答案 4 :(得分:0)
问题(内存使用量激增)可能是由于程序创建输出的速度快于显示速度而导致的。因此,您想限制它。您的问题要求“同步输出”,但实际上可以使用纯“异步”(*)代码来解决问题。
(*注意:在本文中,“异步”一词的含义是“ javascript-单线程”,这与传统的“多线程”含义有所不同,后者是完全不同的水壶鱼)。
此答案显示了“异步”代码如何与Promises一起使用,以通过“暂停”执行(而不是阻塞)来阻止内存使用量激增,直到成功清除写入输出为止。该答案还说明了与异步代码解决方案相比,异步代码解决方案有何优势。
问:“暂停”听起来像“被阻止”,异步代码怎么可能“被阻止”?那是矛盾的事!A:之所以起作用,是因为javascript v8引擎仅暂停(阻止)执行 单个代码片,以等待异步承诺完成,同时允许其他代码片同时执行。 / p>
这是异步写入功能(改编自here)。
async function streamWriteAsync(
stream,
chunk,
encoding='utf8') {
return await new Promise((resolve, reject) => {
const errListener = (err) => {
stream.removeListener('error', errListener);
reject(err);
};
stream.addListener('error', errListener);
const callback = () => {
stream.removeListener('error', errListener);
resolve(undefined);
};
stream.write(chunk, encoding, callback);
});
}
可以从您源代码中的异步函数中调用它,例如
案例1
async function main() {
while (true)
await streamWriteAsync(process.stdout, 'hello world\n')
}
main();
main()
是唯一从顶层调用的函数。内存使用不会像调用console.log('hello world');
那样爆炸。
需要更多上下文才能清楚地看到与真正的同步写入相比的优势:
案例2
async function logger() {
while (true)
await streamWriteAsync(process.stdout, 'hello world\n')
}
const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
function allowOtherThreadsToRun(){
return Promise(resolve => setTimeout(resolve, 0));
}
async function essentialWorker(){
let a=0,b=1;
while (true) {
let tmp=a; a=b; b=tmp;
allowOtherThreadsToRun();
}
}
async function main(){
Promise.all([logger(), essentialWorker()])
}
main();
运行上面的代码(情况2 )将显示内存使用仍未爆炸(与情况1 相同),因为logger
关联的切片暂停,但是CPU使用率仍然是因为essentialWorker
切片没有暂停-很好(请考虑一下COVID)。
相比之下,同步解决方案也会阻止essentialWorker
。
多个切片调用streamWrite
会发生什么?
案例3
async function loggerHi() {
while (true)
await streamWriteAsync(process.stdout, 'hello world\n')
}
async function loggerBye() {
while (true)
await streamWriteAsync(process.stdout, 'goodbye world\n')
}
function allowOtherThreadsToRun(){
return Promise(resolve => setTimeout(resolve, 0));
}
async function essentialWorker(){
let a=0,b=1;
while (true) {
let tmp=a; a=b; b=tmp;
allowOtherThreadsToRun();
}
}
async function main(){
Promise.all([loggerHi(), loggerBye(), essentialWorker()])
}
main();
在这种情况下(情况3 ),绑定了内存使用,并且essentialWorker
CPU使用率很高,与情况2 相同。 hello world
和goodbye world
的各个行将保持原子状态,但这些行将不会清晰地交替显示,例如,
...
hello world
hello world
goodbye world
hello world
hello world
...
可能会出现。