Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node pipe to stdout -- how do I tell if drained?

Tags:

stream

node.js

The standard advice on determine whether you need to wait for drain event on process.stdout is to check whether it returns false when you write to it.

How should I check if I've piped another stream to it? It would seem that that stream can emit finish before all the output is actually written. Can I do something like?

upstreamOfStdout.on('finish', function(){
  if(!process.stdout.write('')) {
    process.stdout.on('drain', function() { done("I'm done"); });
  }
  else {
    done("I'm done"); 
  }
});
upstreamOfStdout.pipe(process.stdout);

I prefer an answer that doesn't depend on the internals of any streams. Just given that the streams conform to the node stream interface, what is the canonical way to do this?

EDIT:

The larger context is a wrapper:

new Promise(function(resolve, reject){
  stream.on(<some-event>, resolve);
  ... (perhaps something else here?)
});

where stream can be process.stdout or something else, which has another through stream piped into it.

My program exits whenever resolve is called -- I presume the Promise code keeps the program alive until all promises have been resolved.

I have encountered this situation several times, and have always used hacks to solve the problem (e.g. there are several private members in process.stdout that are useful.) But I really would like to solve this once and for all (or learn that it is a bug, so I can track the issue and fix my hacks when its resolved, at least): how do I tell when a stream downstream of another is finished processing its input?

like image 340
shaunc Avatar asked Sep 26 '22 11:09

shaunc


1 Answers

Instead of writing directly to process.stdout, create a custom writable (shown below) which writes to stdout as a side effect.

const { Writable } = require('stream');
function writeStdoutAndFinish(){
  return new Writable({
    write(chunk, encoding, callback) {
      process.stdout.write(chunk,callback);
    },
  });
};

The result of writeStdoutAndFinish() will emit a finish event.

async function main(){
  ...
  await new Promise((resolve)=>{
    someReadableStream.pipe(writeStdoutAndFinish()).on('finish',()=>{
      console.log('finish received');
      resolve();
    }) 
  });
  ...
}

In practice, I don't that the above approach differs in behavior from

async function main(){
  ...
  await new Promise((resolve)=>{
    (someReadableStream.on('end',()=>{
      console.log('end received');
      resolve();
    })).pipe(process.stdout)
  });
  ...
}
like image 170
Craig Hicks Avatar answered Oct 12 '22 11:10

Craig Hicks