Skip to content

Instantly share code, notes, and snippets.

@cowboy
Created September 19, 2013 21:53
Show Gist options
  • Save cowboy/6630378 to your computer and use it in GitHub Desktop.
Save cowboy/6630378 to your computer and use it in GitHub Desktop.
Attempting to work around Node.js colossal "fuck you" re. https://github.com/joyent/node/issues/3584
// The idea. This doesn't work, but basically comes
// from https://github.com/gruntjs/grunt/pull/708
var exitCode;
function exit(code) {
if (exitCode === undefined) {
exitcode = code || 0;
tryToExit();
}
}
function isDrained(stream) {
var drained = stream.write('');
if (!drained) { stream.on('drain', tryToExit); }
return drained;
}
function tryToExit() {
if (isDrained(process.stdout) && isDrained(process.stderr)) {
process.exit(exitCode);
}
}
// Test code
for (var i = 0; i < 10; i++) {
console.log("testing %d", i);
}
exit(0);

Both of these need to work in Windows, using Node.js 0.8.x and 0.10.x

C:\exit>node exit.js
testing 0
testing 1
testing 2
testing 3
testing 4
testing 5
testing 6
testing 7
testing 8
testing 9

C:\exit>node exit.js | find "t"
@cowboy
Copy link
Author

cowboy commented Sep 19, 2013

This is SO TOTALLY gross but lets us start to see some output. Not at all reliable.

function tryToExit() {
  setTimeout(function() {
    if (isDrained(process.stdout) && isDrained(process.stderr)) {
      process.exit(exitCode);
    }
  }, 0);
}

@vladikoff
Copy link

function exit(code) {
    var draining = 0;
    var onDrain = function(){
        if (!draining--) process.exit(code);
    };
    if (process.stdout.bufferSize) {
        draining++;
        process.stdout.once('drain', onDrain);
    }
    if (process.stderr.bufferSize) {
        draining++;
        process.stderr.once('drain', onDrain);
    }
    if(!draining)
        process.exit(code);
}

// Test code
for (var i = 0; i < 10; i++) {
  process.stdout.write('testing: ' + i);
  process.stdout.write('\r\n')
}

exit(0);

Can we switch to process.stdout.write?
(tested in 0.10.18)

C:\GitHub\grunt>node z.js
testing: 0
testing: 1
testing: 2
testing: 3
testing: 4
testing: 5
testing: 6
testing: 7
testing: 8
testing: 9

C:\GitHub\grunt>node z.js | find "t"
testing: 0
testing: 1
testing: 2
testing: 3
testing: 4
testing: 5
testing: 6
testing: 7
testing: 8
testing: 9

C:\GitHub\grunt>node z.js | find "0"
testing: 0

C:\GitHub\grunt>node z.js | find "8"
testing: 8

@vladikoff
Copy link

Tested the thing above in 0.8.25. Seems the same.

@cowboy
Copy link
Author

cowboy commented Sep 20, 2013

@vladikoff re. "Can we switch to process.stdout.write?"

  1. we can't assume people will not use console.log in their tasks
  2. Doesn't console.log just write to process.stdout anyways? See console.js

I don't see why there would be a difference... ?

@cowboy
Copy link
Author

cowboy commented Sep 20, 2013

@cowboy
Copy link
Author

cowboy commented Sep 20, 2013

OK, false alarm on nodejs/node-v0.x-archive#6249. Phew. I was going to cry.

@cowboy
Copy link
Author

cowboy commented Sep 20, 2013

Ok, I've rewritten what you posted to be (I think) a little more robust. I've tested it in 0.8.25 and 0.10.18 and it appears to work fine with:

  • node exit.js 10
  • node exit.js 10 2>&1
  • node exit.js 10 2>&1 | find "["
  • node exit.js 10000
  • node exit.js 10000 2>&1
  • node exit.js 10000 2>&1 | find "["
  • node exit.js 10000 2>&1 | find "log"
  • node exit.js 10000 2>&1 | find "err"

What do you think? Can you test it out? If this works, I'll publish it as a standalone lib to npm.

function exit(exitCode) {
  var streams = [process.stdout, process.stderr];
  var drainCount = 0;
  // Actually exit if all streams are drained.
  function tryToExit() {
    if (drainCount === streams.length) {
      process.exit(exitCode);
    }
  }
  streams.forEach(function(stream) {
    // Prevent further writing.
    stream.write = function() {};
    // Count drained streams now, but monitor non-drained streams.
    if (stream.bufferSize === 0) {
      drainCount++;
    } else {
      stream.once('drain', function() {
        drainCount++;
        tryToExit();
      });
    }
  });
  // If all streams were already drained, exit now.
  tryToExit();
}

// Test code
var max = process.argv[2] || 10;
for (var i = 0; i < max; i++) {
  console.log("[log] testing %d", i);
  console.error("[err] testing %d", i);
}

exit(0);

console.log("[log] this shouldn't display");
console.error("[err] this shouldn't display");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment