1

I have a Node v10.14.1 program that reads a CSV file line-by-line using the readline Interface

My .on('line') is an async callback performs some operations which read/write from a db, thus I use async/await to deal with the promises.

A short version of the program's code block of interest would look something like:

const readline = require('readline');
const filesystem = require('fs');

const reader = readline.createInterface({
    input: filesystem.createReadStream(pathToSomeCSV)
});

reader.on('line', async (line) => {
  await doSomeDBStuff();
})

If I leave the above the way it is, the process does not exit. However, if I

reader.on('close', () => {process.exit()});

then the process exits prior to all of the on('line') callbacks finishing and their promises resolving.

My question is: is there a way to say "Upon all lines being read AND all on('line') callbacks being completed with their promises resolved, then exit the process (I assume with process.exit())"?

northsideknight
  • 1,519
  • 1
  • 15
  • 24
  • 1
    Just drop the close handler. When all asynchronous work is done (and there are no outstanding handler), node.js will stop by itself. You don't need to call `process.exit()` explicitly, that's only useful for *prematurely* exiting (or exiting with a specific error code). You should investigate why your process does not end, is some db connection kept open? – Bergi Mar 07 '19 at 14:15
  • so it is an open db connection pool that is causing the process not to exit. I still have the problem but it could be worded differently because it is a problem with db library (sequelize) not readline. – northsideknight Mar 07 '19 at 15:28
  • Yeah. Is there a method that drains sequelizes connection pool? You could call that in the close handler. – Bergi Mar 07 '19 at 15:44
  • I could not find one. What we ended up doing was, for every operation that needs to occur asynchronously, we added a promise to an array that would resolve when that operation completed. Then, upon close we used `await Promise.all(operations)`, when that finished, we explicitly closed the Sequelize connection pool. – northsideknight Mar 08 '19 at 14:14
  • @Bergi dropping the close handler for me keeps the application running forever. This is with [this example](https://nodejs.org/en/knowledge/command-line/how-to-prompt-for-command-line-input/), which is very similar to this question. Keeping the close handler with `process.exit()` however kills the application too soon. Feels like the docs are omitting an important non-obvious piece of information. – aggregate1166877 Oct 28 '22 at 12:59

1 Answers1

0

Investigation

I get the feeling the docs are leaving some non-obvious details out. I was unable to get this official example working correctly (which is what your question appears to be based on). That implementation would kill my application prematurely. Or, if I removed the 'close' listener, the terminal would just hang forever on exit. I tried overriding process.on('exit') to no avail. I also tried the prompt-sync package, but it consistently corrupted my terminal.

Solution

I found a lovely answer here which offers a good solution.

Create the function:

const prompt = msg => {
  fs.writeSync(1, String(msg));
  let s = '', buf = Buffer.alloc(1);
  while(buf[0] - 10 && buf[0] - 13)
    s += buf, fs.readSync(0, buf, 0, 1, 0);
  return s.slice(1);
};

Use it:

const result = prompt('Input something: ');
console.log('Your input was: ' + result);

No terminal corruption, the application does not die prematurely, and it does not hang on exit, either.

This solution is not perfect however - it intentionally blocks the main thread while waiting for user input, meaning you cannot run other functions in the background while waiting for user input. In my mind user input should be thread-blocking in most cases anyway, so this solution works very well for me personally.

Edit: see an improved version for Linux here.

aggregate1166877
  • 2,196
  • 23
  • 38